支持 class 处理,初步版本

pull/1/head
韩天峰 8 months ago
parent 53a8e64b03
commit ca1ee88d5f
  1. 5
      bin/compiler.php
  2. 18
      bin/gen_stub.php
  3. 2
      composer.json
  4. 724
      composer.lock
  5. 22
      examples/class/MyClass.php
  6. 6
      examples/class/main.php
  7. 23
      examples/class/test.php
  8. 5
      src/Core/Translator.php
  9. 12
      src/Php/ClassDef.php
  10. 164
      src/Php/CompilerBase.php
  11. 2
      src/Php/ConstantDef.php
  12. 16
      src/Php/MethodDef.php
  13. 20
      src/Php/Preprocessor.php
  14. 262
      src/Php/Translator.php

@ -68,8 +68,9 @@ if (empty($sourceFiles)) {
$translator->stop("No valid source file found"); $translator->stop("No valid source file found");
} }
// 生成所有函数声明 // 生成所有函数声明、全局变量声明
$translator->genFunctionDeclaration($translator->getBuildDir() . "/include/php_func_decl.h"); $translator->genFunctionDeclaration($translator->getIncludeDir() . '/php_func_decl.h');
$translator->genExternGlobalVars($translator->getIncludeDir() . '/php_global_var_decl.h');
// 生成所有全局变量源文件 // 生成所有全局变量源文件
$globalVarsSourceFile = $translator->getBuildDir() . '/global_vars.cc'; $globalVarsSourceFile = $translator->getBuildDir() . '/global_vars.cc';

@ -20,6 +20,10 @@ error_reporting(E_ALL);
ini_set("precision", "-1"); ini_set("precision", "-1");
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';
$translator = new PhpAot\Php\Translator(ROOT_PATH);
$translator->setIndent("\t");
$translator->setIndentLevel(1);
const PHP_70_VERSION_ID = 70000; const PHP_70_VERSION_ID = 70000;
const PHP_80_VERSION_ID = 80000; const PHP_80_VERSION_ID = 80000;
const PHP_81_VERSION_ID = 80100; const PHP_81_VERSION_ID = 80100;
@ -80,8 +84,9 @@ function processStubFile(string $stubFile, Context $context, bool $includeOnly =
} }
if (!$includeOnly) { if (!$includeOnly) {
$stubFilenameWithoutExtension = str_replace(".stub.php", "", $stubFile); global $translator;
$arginfoFile = "{$stubFilenameWithoutExtension}_arginfo.h"; $stubFilenameWithoutExtension = str_replace([".stub.php", '.php'], "", $stubFile);
$arginfoFile = $translator->getArgInfoHeaderFile($stubFilenameWithoutExtension);
$legacyFile = "{$stubFilenameWithoutExtension}_legacy_arginfo.h"; $legacyFile = "{$stubFilenameWithoutExtension}_legacy_arginfo.h";
$stubCode = file_get_contents($stubFile); $stubCode = file_get_contents($stubFile);
@ -2399,7 +2404,10 @@ class EvaluatedValue
if ($cExpr == '[]') { if ($cExpr == '[]') {
$code .= "\tZVAL_EMPTY_ARRAY(&$zvalName);\n"; $code .= "\tZVAL_EMPTY_ARRAY(&$zvalName);\n";
} else { } else {
throw new Exception("Unimplemented default value"); global $translator;
$tmpVar = $translator->genTmpVarName();
$code .= "\tauto $tmpVar = " . $translator->parseExpr($this->expr) . ";\n";
$code .= "\t{$tmpVar}.moveTo(&$zvalName);\n";
} }
} else { } else {
throw new Exception("Invalid default value: " . print_r($this->value, true) . ", type: " . print_r($this->type, true)); throw new Exception("Invalid default value: " . print_r($this->value, true) . ", type: " . print_r($this->type, true));
@ -4192,7 +4200,7 @@ class FileInfo {
public array $classInfos = []; public array $classInfos = [];
public bool $generateFunctionEntries = false; public bool $generateFunctionEntries = false;
public string $declarationPrefix = ""; public string $declarationPrefix = "";
public bool $generateClassEntries = false; public bool $generateClassEntries = true;
private bool $isUndocumentable = false; private bool $isUndocumentable = false;
private bool $legacyArginfoGeneration = false; private bool $legacyArginfoGeneration = false;
private ?int $minimumPhpVersionIdCompatibility = null; private ?int $minimumPhpVersionIdCompatibility = null;
@ -4429,7 +4437,7 @@ class FileInfo {
} }
} else if ($classStmt instanceof Stmt\ClassMethod) { } else if ($classStmt instanceof Stmt\ClassMethod) {
if (!($classStmt->flags & Class_::VISIBILITY_MODIFIER_MASK)) { if (!($classStmt->flags & Class_::VISIBILITY_MODIFIER_MASK)) {
throw new Exception("Visibility modifier is required"); $classStmt->flags |= Modifiers::PUBLIC;
} }
$methodInfos[] = parseFunctionLike( $methodInfos[] = parseFunctionLike(
$prettyPrinter, $prettyPrinter,

@ -1,6 +1,6 @@
{ {
"require": { "require": {
"nikic/php-parser": "^5.0", "nikic/php-parser": "5.6.1",
"league/climate": "^3.10" "league/climate": "^3.10"
}, },
"require-dev": { "require-dev": {

724
composer.lock generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,22 @@
<?php
namespace Test2;
class MyClass
{
private string $name;
private int $a;
private int $b;
function __construct(int $a, int $b, string $name)
{
$this->name = $name;
$this->a = $a;
$this->b = $b;
}
function sum(): int
{
return $this->a + $this->b;
}
}

@ -0,0 +1,6 @@
<?php
function main()
{
var_dump("hello");
}

@ -6,6 +6,23 @@ class Test
private string $b; private string $b;
private int $c = 0; private int $c = 0;
const int T_E = 1; public string $d = 'hello';
const T_S = 'hello'; public array $e = [1, 2, 3];
}
public const int T_E = 1;
protected const T_S = 'hello';
private const T_A = [1, 2, 3];
public function __construct()
{
$this->a = 1;
$this->b = 'hello';
$this->c = 0;
}
public function test(int $a, int $b)
{
return $this->a + $this->b + $a + $b;
}
}

@ -19,6 +19,11 @@ abstract class Translator
$this->indentStr = $indent; $this->indentStr = $indent;
} }
public function setIndentLevel(int $level): void
{
$this->indentLevel = $level;
}
public function getLang(): string public function getLang(): string
{ {
return $this->lang; return $this->lang;

@ -5,10 +5,16 @@ namespace PhpAot\Php;
class ClassDef class ClassDef
{ {
public string $name; public string $name;
public array $methods; /**
* @var array< MethodDef>
*/
public array $methods = [];
/** /**
* @var array<PropertyDef> * @var array<PropertyDef>
*/ */
public array $properties; public array $properties = [];
public array $constants; /**
* @var array<ConstantDef>
*/
public array $constants = [];
} }

@ -6,6 +6,7 @@ use League\CLImate\CLImate;
use PhpAot\Php\Visitor; use PhpAot\Php\Visitor;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\Expr\Variable; use PhpParser\Node\Expr\Variable;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Identifier; use PhpParser\Node\Identifier;
use PhpParser\Error; use PhpParser\Error;
use PhpParser\Node\NullableType; use PhpParser\Node\NullableType;
@ -52,20 +53,20 @@ class CompilerBase extends \PhpAot\Core\Translator
'bool' => self::TYPE_BOOL, 'bool' => self::TYPE_BOOL,
]; ];
protected array $headers = [
'phpx.h',
'phpx_helper.h',
'phpx_func.h',
'php_func_decl.h',
];
protected array $reservedNames; protected array $reservedNames;
protected array $unsupportedFunctions = [ protected array $unsupportedFunctions = [
'compact', 'compact',
'extract' 'extract'
]; ];
protected array $globalHeaders = [
'phpx.h',
'phpx_helper.h',
'phpx_func.h',
'php_func_decl.h',
'php_global_var_decl.h',
];
protected array $localHeaders = [];
protected array $nativeFunctions = []; protected array $nativeFunctions = [];
protected array $internalFunctions = []; protected array $internalFunctions = [];
protected array $nativeConstants = []; protected array $nativeConstants = [];
@ -112,6 +113,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected bool $inLoop = false; protected bool $inLoop = false;
protected bool $inSwitch = false; protected bool $inSwitch = false;
protected bool $stubFile = false; protected bool $stubFile = false;
protected bool $stubFileIncluded = false;
protected Parser $parser; protected Parser $parser;
public function __construct(string $rootPath) public function __construct(string $rootPath)
@ -126,45 +128,24 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->noLiteralStrings = true; $this->noLiteralStrings = true;
} }
public function genIncludeHeaderFiles(): string
{
$lines = [];
foreach ($this->headers as $header) {
$lines[] = '#include <' . $header . '>';
}
return implode(PHP_EOL, $lines) . PHP_EOL . PHP_EOL;
}
public function genExternGlobalVars(): string
{
$lines[] = PHP_EOL;
foreach ($this->globalVars as $name => $type) {
$lines[] = 'extern ' . self::TYPE_VAR . ' ' . $name . ';';
}
$literalStringsCount = count($this->literalStrings);
$lines[] = 'extern php::Var ' . self::LITERAL_STRINGS . '[' . $literalStringsCount . '];' . PHP_EOL;
return implode(PHP_EOL, $lines) . PHP_EOL . PHP_EOL;
}
public function setPhpxDir($dir): void public function setPhpxDir($dir): void
{ {
$this->phpxDir = $dir; $this->phpxDir = $dir;
} }
protected function removeCommonPrefix(string $str1, string $str2): string protected function removeCommonPrefix(string $short, string $long): string
{ {
$len = min(strlen($str1), strlen($str2)); $len = min(strlen($short), strlen($long));
$prefixLen = 0; $prefixLen = 0;
for ($i = 0; $i < $len; $i++) { for ($i = 0; $i < $len; $i++) {
if ($str1[$i] === $str2[$i]) { if ($short[$i] === $long[$i]) {
$prefixLen++; $prefixLen++;
} else { } else {
break; break;
} }
} }
return substr($str2, $prefixLen); return substr($long, $prefixLen);
} }
public function save(string $code, string $file): void public function save(string $code, string $file): void
@ -211,6 +192,14 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->tmpVarIndex = 0; $this->tmpVarIndex = 0;
} }
protected function resetFile(): void
{
$this->indentLevel = 0;
$this->strictTypes = false;
$this->stubFileIncluded = false;
$this->localHeaders = [];
}
protected function resetNamespace(): void protected function resetNamespace(): void
{ {
$this->useNamespaces = []; $this->useNamespaces = [];
@ -221,16 +210,16 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function getFunctionName(Node $v): string protected function getFunctionName(Node $v): string
{ {
$names[] = strtolower($this->parseIdentifier($v->name)); $names[] = strtolower($this->parseIdentifier($v->name));
if ($this->class) {
$names[] = strtolower($this->class);
}
if ($this->namespace) { if ($this->namespace) {
$names[] = $this->escapeNamespace($this->namespace); $names[] = $this->escapeNamespace($this->namespace);
} }
if ($this->class) {
$names[] = $this->escapeClass($this->class);
}
return implode(self::NAMESPACE_SEPARATOR, array_reverse($names)); return implode(self::NAMESPACE_SEPARATOR, array_reverse($names));
} }
protected function parseFunctionDeclaration(string $name, Node $v): FunctionDef protected function parseFunctionDeclaration(string $name, Node\FunctionLike $v): FunctionDef
{ {
$functionDef = new FunctionDef(); $functionDef = new FunctionDef();
$this->functionDef = $functionDef; $this->functionDef = $functionDef;
@ -248,7 +237,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return $functionDef; return $functionDef;
} }
protected function parseFunctionDef(Node $v): string protected function parseFunction(Node\FunctionLike $v): string
{ {
$this->resetScope(); $this->resetScope();
$name = $this->getFunctionName($v); $name = $this->getFunctionName($v);
@ -270,6 +259,10 @@ class CompilerBase extends \PhpAot\Core\Translator
} }
} }
if ($this->class) {
$this->arguments['this_'] = self::TYPE_OBJECT;
}
if ($v->stmts) { if ($v->stmts) {
$this->indentLevel++; $this->indentLevel++;
$stmts = $this->parseStmts($v->stmts); $stmts = $this->parseStmts($v->stmts);
@ -278,7 +271,14 @@ class CompilerBase extends \PhpAot\Core\Translator
$stmts = ''; $stmts = '';
} }
$functionDeclCode = $this->getReturnType() . ' ' . self::PREFIX . $name . '(' . $this->functionDef->params . ')'; $functionDeclCode = $this->getReturnType() . ' ' . self::PREFIX . $name . '(';
if ($this->class) {
$functionDeclCode .= self::TYPE_OBJECT . ' &this_';
if ($this->functionDef->params) {
$functionDeclCode .= ', ';
}
}
$functionDeclCode .= $this->functionDef->params . ')';
$code = $functionDeclCode . ' {' . PHP_EOL; $code = $functionDeclCode . ' {' . PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
@ -460,7 +460,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return $code; return $code;
} }
protected function parseExpr(mixed $expr) public function parseExpr(mixed $expr)
{ {
$type = $expr->getType(); $type = $expr->getType();
$this->writeLog('Line ' . $this->getLine($expr) . ': ' . $type); $this->writeLog('Line ' . $this->getLine($expr) . ': ' . $type);
@ -987,7 +987,10 @@ class CompilerBase extends \PhpAot\Core\Translator
return self::TYPE_FLOAT; return self::TYPE_FLOAT;
case 'bool': case 'bool':
return self::TYPE_BOOL; return self::TYPE_BOOL;
case 'string':
return self::TYPE_STR;
default: default:
var_dump($name);
abort($type); abort($type);
} }
} }
@ -1703,13 +1706,13 @@ class CompilerBase extends \PhpAot\Core\Translator
$type2 = $v2->getType(); $type2 = $v2->getType();
switch ($type2) { switch ($type2) {
case 'Stmt_Class': case 'Stmt_Class':
$code .= $this->parseClassDef($v2); $code .= $this->parseClass($v2);
break; break;
case 'Stmt_Const': case 'Stmt_Const':
$this->parseConstDef($v2) . PHP_EOL; $this->parseConstDef($v2) . PHP_EOL;
break; break;
case 'Stmt_Function': case 'Stmt_Function':
$code .= $this->parseFunctionDef($v2) . PHP_EOL; $code .= $this->parseFunction($v2) . PHP_EOL;
break; break;
case 'Stmt_Use': case 'Stmt_Use':
$code .= $this->parseUse($v2) . PHP_EOL; $code .= $this->parseUse($v2) . PHP_EOL;
@ -1723,33 +1726,6 @@ class CompilerBase extends \PhpAot\Core\Translator
return $code; return $code;
} }
protected function parseClassDef(Node $v): string
{
$this->class = $this->parseIdentifier($v->name);
$this->classDef = new ClassDef();
$this->classDef->name = $this->class;
$code = 'class ' . $this->class . ' { ';
foreach ($v->stmts as $v) {
$type = $v->getType();
switch ($type) {
case 'Stmt_ClassConst':
$code .= $this->parseClassConstDef($v);
break;
case 'Stmt_Property':
$this->parsePropertyDef($v);
break;
case 'Stmt_ClassMethod':
$code .= $this->parseFunctionDef($v) . PHP_EOL;
break;
default:
abort($v);
}
}
$code .= '}';
$this->class = '';
return $code;
}
protected function parseCastInt(Node $node): string protected function parseCastInt(Node $node): string
{ {
return $this->convertIntExpr($this->parseExpr($node->expr)); return $this->convertIntExpr($this->parseExpr($node->expr));
@ -1833,6 +1809,8 @@ class CompilerBase extends \PhpAot\Core\Translator
{ {
if (in_array($name, Constants::CPP_RESERVED_NAMES)) { if (in_array($name, Constants::CPP_RESERVED_NAMES)) {
return '_php__var__' . $name; return '_php__var__' . $name;
} elseif ($name === 'this') {
return 'this_';
} else { } else {
return $name; return $name;
} }
@ -1843,6 +1821,11 @@ class CompilerBase extends \PhpAot\Core\Translator
return str_replace('\\', self::NAMESPACE_SEPARATOR, strtolower($ns)); return str_replace('\\', self::NAMESPACE_SEPARATOR, strtolower($ns));
} }
protected function escapeClass(string $class): string
{
return $class;
}
protected function unescapeVarName(string $name): string protected function unescapeVarName(string $name): string
{ {
return str_replace('_php__var__', '', $name); return str_replace('_php__var__', '', $name);
@ -2022,7 +2005,7 @@ class CompilerBase extends \PhpAot\Core\Translator
shell_exec($cmd); shell_exec($cmd);
} }
protected function genTmpVarName(): string public function genTmpVarName(): string
{ {
return 'tmp_var_' . $this->tmpVarIndex++; return 'tmp_var_' . $this->tmpVarIndex++;
} }
@ -2182,18 +2165,6 @@ class CompilerBase extends \PhpAot\Core\Translator
return array_key_exists($name, $this->globalVars); return array_key_exists($name, $this->globalVars);
} }
protected function genFunction(string $name, string $returnType, array $args = [], array $lines = []): string
{
$_args = [];
foreach ($args as $arg => $type) {
$_args[] = $type . ' ' . $arg;
}
$code = $returnType . ' ' . $name . '(' . implode(', ', $_args) . ') {' . PHP_EOL;
$code .= implode(PHP_EOL, $lines) . PHP_EOL;
$code .= '}' . PHP_EOL;
return $code;
}
protected function parseCastDouble(mixed $expr): string protected function parseCastDouble(mixed $expr): string
{ {
return $this->convertFloatExpr($this->parseIdentifier($expr->expr)); return $this->convertFloatExpr($this->parseIdentifier($expr->expr));
@ -2499,6 +2470,11 @@ class CompilerBase extends \PhpAot\Core\Translator
} }
} }
public function getIncludeDir(): string
{
return $this->getBuildDir() . '/include';
}
public function getBuildDir(): string public function getBuildDir(): string
{ {
return $this->buildDir; return $this->buildDir;
@ -2552,28 +2528,4 @@ class CompilerBase extends \PhpAot\Core\Translator
} }
abort($expr); abort($expr);
} }
protected function parsePropertyDef(Node\Stmt\Property $v): void
{
$flags = $v->flags;
$type = $v->type ? $this->getTypeFromZendType($this->parseIdentifier($v->type)) : self::TYPE_VAR;
foreach ($v->props as $prop) {
$propInfo = new PropertyDef($this->parseIdentifier($prop->name), $flags, $type);
if ($prop->default) {
$this->parseIdentifier($prop->default);
}
$this->classDef->properties[] = $propInfo;
}
}
protected function parseClassConstDef(Node\Stmt\ClassConst $v): void
{
$flags = $v->flags;
$type = $v->type ? $this->getTypeFromZendType($this->parseIdentifier($v->type)) : self::TYPE_VAR;
foreach ($v->consts as $const) {
$constInfo = new ConstDef($this->parseIdentifier($const->name), $flags, $type, $this->parseIdentifier($const->value));
$this->classDef->constants[] = $constInfo;
}
}
} }

@ -2,7 +2,7 @@
namespace PhpAot\Php; namespace PhpAot\Php;
class ConstDef class ConstantDef
{ {
public string $name; public string $name;
public string $type; public string $type;

@ -0,0 +1,16 @@
<?php
namespace PhpAot\Php;
class MethodDef
{
public string $name;
public string $flags;
public function __construct(string $name, string $flags)
{
$this->name = $name;
$this->flags = $flags;
}
}

@ -44,10 +44,10 @@ class Preprocessor extends CompilerBase
$this->prepareNamespaceDef($v); $this->prepareNamespaceDef($v);
break; break;
case 'Stmt_Class': case 'Stmt_Class':
$this->prepareClassDef($v); $this->prepareClass($v);
break; break;
case 'Stmt_Function': case 'Stmt_Function':
$this->prepareFunctionDef($v) . PHP_EOL; $this->prepareFunction($v) . PHP_EOL;
break; break;
case 'Stmt_Declare': case 'Stmt_Declare':
case 'Stmt_Use': case 'Stmt_Use':
@ -74,7 +74,7 @@ class Preprocessor extends CompilerBase
} }
} }
protected function prepareFunctionDef(Node $v): void protected function prepareFunction(Node $v): void
{ {
$name = $this->getFunctionName($v); $name = $this->getFunctionName($v);
if ($this->stubFile) { if ($this->stubFile) {
@ -84,7 +84,7 @@ class Preprocessor extends CompilerBase
} }
} }
protected function prepareNamespaceDef(Node $node): void protected function prepareNamespaceDef(Node\Stmt\Namespace_ $node): void
{ {
$this->resetNamespace(); $this->resetNamespace();
$this->namespace = $this->escapeNamespace($this->parseIdentifier($node->name)); $this->namespace = $this->escapeNamespace($this->parseIdentifier($node->name));
@ -92,10 +92,10 @@ class Preprocessor extends CompilerBase
$type2 = $v2->getType(); $type2 = $v2->getType();
switch ($type2) { switch ($type2) {
case 'Stmt_Class': case 'Stmt_Class':
$this->prepareClassDef($v2); $this->prepareClass($v2);
break; break;
case 'Stmt_Function': case 'Stmt_Function':
$this->prepareFunctionDef($v2) . PHP_EOL; $this->prepareFunction($v2) . PHP_EOL;
break; break;
case 'Stmt_Use': case 'Stmt_Use':
case 'Stmt_Const': case 'Stmt_Const':
@ -107,18 +107,18 @@ class Preprocessor extends CompilerBase
$this->resetNamespace(); $this->resetNamespace();
} }
protected function prepareClassDef(Node $v): string protected function prepareClass(Node\Stmt\Class_ $class): string
{ {
$this->class = $this->parseIdentifier($v->name); $this->class = $this->parseIdentifier($class->name);
$code = ''; $code = '';
foreach ($v->stmts as $v) { foreach ($class->stmts as $v) {
$type = $v->getType(); $type = $v->getType();
switch ($type) { switch ($type) {
case 'Stmt_ClassConst': case 'Stmt_ClassConst':
case 'Stmt_Property': case 'Stmt_Property':
break; break;
case 'Stmt_ClassMethod': case 'Stmt_ClassMethod':
$code .= $this->prepareFunctionDef($v) . PHP_EOL; $code .= $this->prepareFunction($v) . PHP_EOL;
break; break;
default: default:
abort($v); abort($v);

@ -2,6 +2,8 @@
namespace PhpAot\Php; namespace PhpAot\Php;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\NodeTraverser; use PhpParser\NodeTraverser;
class Translator extends Preprocessor class Translator extends Preprocessor
@ -112,8 +114,7 @@ class Translator extends Preprocessor
$stmts = $traverser->traverse($ast); $stmts = $traverser->traverse($ast);
$this->indentLevel = 0; $this->resetFile();
$this->strictTypes = false;
$this->resetNamespace(); $this->resetNamespace();
$cppCode = ''; $cppCode = '';
@ -127,13 +128,13 @@ class Translator extends Preprocessor
$cppCode .= $this->parseNamespaceDef($v); $cppCode .= $this->parseNamespaceDef($v);
break; break;
case 'Stmt_Class': case 'Stmt_Class':
$cppCode .= $this->parseClassDef($v); $cppCode .= $this->parseClass($v);
break; break;
case 'Stmt_Use': case 'Stmt_Use':
$cppCode .= $this->parseUse($v) . PHP_EOL; $cppCode .= $this->parseUse($v) . PHP_EOL;
break; break;
case 'Stmt_Function': case 'Stmt_Function':
$cppCode .= $this->parseFunctionDef($v) . PHP_EOL; $cppCode .= $this->parseFunction($v) . PHP_EOL;
break; break;
case 'Stmt_Const': case 'Stmt_Const':
$this->parseConstDef($v) . PHP_EOL; $this->parseConstDef($v) . PHP_EOL;
@ -143,7 +144,7 @@ class Translator extends Preprocessor
} }
} }
// include + extern global vars + function impl // include + extern global vars + function impl
return $this->genIncludeHeaderFiles() . $this->genExternGlobalVars() . $cppCode; return $this->genIncludeHeaderFiles() . $cppCode;
} }
public function preprocessArgvAdvanced(): void public function preprocessArgvAdvanced(): void
@ -170,6 +171,20 @@ class Translator extends Preprocessor
$argv = $processed; $argv = $processed;
} }
public function genExternGlobalVars(string $file): void
{
$lines[] = '#include <phpx.h>';
$lines[] = PHP_EOL;
foreach ($this->globalVars as $name => $type) {
$lines[] = 'extern ' . self::TYPE_VAR . ' ' . $name . ';';
}
$literalStringsCount = count($this->literalStrings);
$lines[] = 'extern php::Var ' . self::LITERAL_STRINGS . '[' . $literalStringsCount . '];' . PHP_EOL;
$code = implode(PHP_EOL, $lines) . PHP_EOL . PHP_EOL;
$this->writeFile($file, $code);
}
public function genGlobalVars(string $file): void public function genGlobalVars(string $file): void
{ {
$code = $this->genIncludeHeaderFiles(); $code = $this->genIncludeHeaderFiles();
@ -260,7 +275,7 @@ class Translator extends Preprocessor
public function genFunctionDeclaration(string $file): void public function genFunctionDeclaration(string $file): void
{ {
$code = ''; $code = '#include <phpx.h>' . PHP_EOL;
/** /**
* @var FunctionDef $func * @var FunctionDef $func
*/ */
@ -288,4 +303,239 @@ class Translator extends Preprocessor
$this->writeFile($file, $code); $this->writeFile($file, $code);
} }
protected function getMethodName(Node\Stmt\ClassMethod $v): string
{
return strtolower($this->parseIdentifier($v->name));
}
protected function parseClass(Node\Stmt\Class_ $class): string
{
$this->class = $this->parseIdentifier($class->name);
if (!$this->stubFileIncluded) {
shell_exec('php ' . $this->rootPath . '/bin/gen_stub.php -f' . $this->file);
$stubFilenameWithoutExtension = str_replace([".stub.php", '.php'], "", $this->file);
$this->localHeaders[] = $this->getArgInfoHeaderFile($stubFilenameWithoutExtension, true);
$this->stubFileIncluded = true;
}
$this->classDef = new ClassDef();
$this->classDef->name = $this->class;
$methodCodes = [];
foreach ($class->stmts as $v) {
$type = $v->getType();
switch ($type) {
case 'Stmt_ClassConst':
$this->parseClassConstDef($v);
break;
case 'Stmt_Property':
$this->parsePropertyDef($v);
break;
case 'Stmt_ClassMethod':
$this->parseClassMethod($v, $methodCodes);
break;
default:
abort($v);
}
}
$code = $this->genZendClass($methodCodes);
$this->class = '';
return $code;
}
public function getArgInfoHeaderFile(string $stubFilenameWithoutExtension, bool $relative = false): string
{
$basename = basename($stubFilenameWithoutExtension);
$absPath = $this->getIncludeDir() . "/{$basename}_arginfo.h";
if ($relative) {
return ltrim($this->removeCommonPrefix($this->getIncludeDir(), $absPath), '/');
} else {
return $absPath;
}
}
protected function genZendClass($methodCodes): string
{
$code = '';
$classDef = $this->classDef;
foreach ($classDef->methods as $method) {
$code .= $methodCodes[$method->name] . PHP_EOL;
}
return $code;
}
private function genClassNative(): string
{
$code = 'class ' . $this->class . ' { ';
$publicMethods = [];
$protectedMethods = [];
$privateMethods = [];
$publicConstants = [];
$protectedConstants = [];
$privateConstants = [];
$publicProperties = [];
$protectedProperties = [];
$privateProperties = [];
foreach ($this->classDef->constants as $const) {
if ($const->flags & Modifiers::PUBLIC) {
$publicConstants[] = $const;
}
if ($const->flags & Modifiers::PROTECTED) {
$protectedConstants[] = $const;
}
if ($const->flags & Modifiers::PRIVATE) {
$privateConstants[] = $const;
}
}
foreach ($this->classDef->methods as $method) {
if ($method->flags & Modifiers::PUBLIC) {
$publicMethods[] = $method;
}
if ($method->flags & Modifiers::PROTECTED) {
$protectedMethods[] = $method;
}
if ($method->flags & Modifiers::PRIVATE) {
$privateMethods[] = $method;
}
}
foreach ($this->classDef->properties as $property) {
if ($property->flags & Modifiers::PUBLIC) {
$publicProperties[] = $property;
}
if ($property->flags & Modifiers::PROTECTED) {
$protectedProperties[] = $property;
}
if ($property->flags & Modifiers::PRIVATE) {
$privateProperties[] = $property;
}
}
if ($privateConstants) {
$code .= 'private:' . PHP_EOL;
$code .= $this->genClassConstantList($privateConstants);
}
if ($protectedConstants) {
$code .= 'protected:' . PHP_EOL;
$code .= $this->genClassConstantList($protectedConstants);
}
if ($publicConstants) {
$code .= 'public:' . PHP_EOL;
$code .= $this->genClassConstantList($publicConstants);
}
if ($privateProperties) {
$code .= 'private:' . PHP_EOL;
$code .= $this->genClassPropertyList($privateProperties);
}
if ($protectedProperties) {
$code .= 'protected:' . PHP_EOL;
$code .= $this->genClassPropertyList($protectedProperties);
}
if ($publicProperties) {
$code .= 'public:' . PHP_EOL;
$code .= $this->genClassPropertyList($publicProperties);
}
$code .= '};' . PHP_EOL . PHP_EOL;
return $code;
}
public function genIncludeHeaderFiles(): string
{
$headers = array_merge($this->globalHeaders, $this->localHeaders);
$lines = [];
foreach ($headers as $header) {
$lines[] = '#include <' . $header . '>';
}
return implode(PHP_EOL, $lines) . PHP_EOL . PHP_EOL;
}
/**
* @param array<ConstantDef> $list
*/
protected function genClassConstantList(array $list): string
{
$code = '';
foreach ($list as $const) {
$code .= $this->getIndent() . $this->genClassConstant($const);
}
return $code;
}
protected function genClassConstant(ConstantDef $const): string
{
return 'static const ' . $const->type . ' ' . $const->name . ';' . PHP_EOL;
}
/**
* @param array<PropertyDef> $list
*/
protected function genClassPropertyList(array $list): string
{
$code = '';
foreach ($list as $prop) {
$code .= $this->getIndent() . $this->genClassProperty($prop);
}
return $code;
}
protected function genClassProperty(PropertyDef $prop): string
{
$code = $prop->type . ' ' . $prop->name;
if ($prop->default) {
$code .= ' = ' . $prop->default;
}
return $code . ';' . PHP_EOL;
}
protected function genFunction(string $name, string $returnType, array $args = [], array $lines = []): string
{
$_args = [];
foreach ($args as $arg => $type) {
$_args[] = $type . ' ' . $arg;
}
$code = $returnType . ' ' . $name . '(' . implode(', ', $_args) . ') {' . PHP_EOL;
$code .= implode(PHP_EOL, $lines) . PHP_EOL;
$code .= '}' . PHP_EOL;
return $code;
}
protected function parseClassConstDef(Node\Stmt\ClassConst $v): void
{
$flags = $v->flags;
$type = $v->type ? $this->getTypeFromZendType($this->parseIdentifier($v->type)) : self::TYPE_VAR;
foreach ($v->consts as $const) {
$constInfo = new ConstantDef($this->parseIdentifier($const->name), $flags, $type, $this->parseIdentifier($const->value));
$this->classDef->constants[] = $constInfo;
}
}
protected function parsePropertyDef(Node\Stmt\Property $v): void
{
$flags = $v->flags;
$type = $v->type ? $this->getTypeFromZendType($this->parseIdentifier($v->type)) : self::TYPE_VAR;
foreach ($v->props as $prop) {
$propDef = new PropertyDef($this->parseIdentifier($prop->name), $flags, $type);
if ($prop->default) {
$propDef->default = $this->parseIdentifier($prop->default);
}
$this->classDef->properties[] = $propDef;
}
}
private function parseClassMethod(Node\Stmt\ClassMethod $v, array &$methodCodes): void
{
$name = $this->getMethodName($v);
$flags = $v->flags;
$methodDef = new MethodDef($name, $flags);
$this->classDef->methods[] = $methodDef;
$methodCodes[$name] = $this->parseFunction($v);
}
} }
Loading…
Cancel
Save