支持 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->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';

@ -20,6 +20,10 @@ error_reporting(E_ALL);
ini_set("precision", "-1");
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_80_VERSION_ID = 80000;
const PHP_81_VERSION_ID = 80100;
@ -80,8 +84,9 @@ function processStubFile(string $stubFile, Context $context, bool $includeOnly =
}
if (!$includeOnly) {
$stubFilenameWithoutExtension = str_replace(".stub.php", "", $stubFile);
$arginfoFile = "{$stubFilenameWithoutExtension}_arginfo.h";
global $translator;
$stubFilenameWithoutExtension = str_replace([".stub.php", '.php'], "", $stubFile);
$arginfoFile = $translator->getArgInfoHeaderFile($stubFilenameWithoutExtension);
$legacyFile = "{$stubFilenameWithoutExtension}_legacy_arginfo.h";
$stubCode = file_get_contents($stubFile);
@ -2399,7 +2404,10 @@ class EvaluatedValue
if ($cExpr == '[]') {
$code .= "\tZVAL_EMPTY_ARRAY(&$zvalName);\n";
} 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 {
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 bool $generateFunctionEntries = false;
public string $declarationPrefix = "";
public bool $generateClassEntries = false;
public bool $generateClassEntries = true;
private bool $isUndocumentable = false;
private bool $legacyArginfoGeneration = false;
private ?int $minimumPhpVersionIdCompatibility = null;
@ -4429,7 +4437,7 @@ class FileInfo {
}
} else if ($classStmt instanceof Stmt\ClassMethod) {
if (!($classStmt->flags & Class_::VISIBILITY_MODIFIER_MASK)) {
throw new Exception("Visibility modifier is required");
$classStmt->flags |= Modifiers::PUBLIC;
}
$methodInfos[] = parseFunctionLike(
$prettyPrinter,

@ -1,6 +1,6 @@
{
"require": {
"nikic/php-parser": "^5.0",
"nikic/php-parser": "5.6.1",
"league/climate": "^3.10"
},
"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 int $c = 0;
const int T_E = 1;
const T_S = 'hello';
}
public string $d = '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;
}
public function setIndentLevel(int $level): void
{
$this->indentLevel = $level;
}
public function getLang(): string
{
return $this->lang;

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

@ -6,6 +6,7 @@ use League\CLImate\CLImate;
use PhpAot\Php\Visitor;
use PhpParser\Node;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Identifier;
use PhpParser\Error;
use PhpParser\Node\NullableType;
@ -52,20 +53,20 @@ class CompilerBase extends \PhpAot\Core\Translator
'bool' => self::TYPE_BOOL,
];
protected array $headers = [
'phpx.h',
'phpx_helper.h',
'phpx_func.h',
'php_func_decl.h',
];
protected array $reservedNames;
protected array $unsupportedFunctions = [
'compact',
'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 $internalFunctions = [];
protected array $nativeConstants = [];
@ -112,6 +113,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected bool $inLoop = false;
protected bool $inSwitch = false;
protected bool $stubFile = false;
protected bool $stubFileIncluded = false;
protected Parser $parser;
public function __construct(string $rootPath)
@ -126,45 +128,24 @@ class CompilerBase extends \PhpAot\Core\Translator
$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
{
$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;
for ($i = 0; $i < $len; $i++) {
if ($str1[$i] === $str2[$i]) {
if ($short[$i] === $long[$i]) {
$prefixLen++;
} else {
break;
}
}
return substr($str2, $prefixLen);
return substr($long, $prefixLen);
}
public function save(string $code, string $file): void
@ -211,6 +192,14 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->tmpVarIndex = 0;
}
protected function resetFile(): void
{
$this->indentLevel = 0;
$this->strictTypes = false;
$this->stubFileIncluded = false;
$this->localHeaders = [];
}
protected function resetNamespace(): void
{
$this->useNamespaces = [];
@ -221,16 +210,16 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function getFunctionName(Node $v): string
{
$names[] = strtolower($this->parseIdentifier($v->name));
if ($this->class) {
$names[] = strtolower($this->class);
}
if ($this->namespace) {
$names[] = $this->escapeNamespace($this->namespace);
}
if ($this->class) {
$names[] = $this->escapeClass($this->class);
}
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();
$this->functionDef = $functionDef;
@ -248,7 +237,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return $functionDef;
}
protected function parseFunctionDef(Node $v): string
protected function parseFunction(Node\FunctionLike $v): string
{
$this->resetScope();
$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) {
$this->indentLevel++;
$stmts = $this->parseStmts($v->stmts);
@ -278,7 +271,14 @@ class CompilerBase extends \PhpAot\Core\Translator
$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;
$this->indentLevel++;
@ -460,7 +460,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return $code;
}
protected function parseExpr(mixed $expr)
public function parseExpr(mixed $expr)
{
$type = $expr->getType();
$this->writeLog('Line ' . $this->getLine($expr) . ': ' . $type);
@ -987,7 +987,10 @@ class CompilerBase extends \PhpAot\Core\Translator
return self::TYPE_FLOAT;
case 'bool':
return self::TYPE_BOOL;
case 'string':
return self::TYPE_STR;
default:
var_dump($name);
abort($type);
}
}
@ -1703,13 +1706,13 @@ class CompilerBase extends \PhpAot\Core\Translator
$type2 = $v2->getType();
switch ($type2) {
case 'Stmt_Class':
$code .= $this->parseClassDef($v2);
$code .= $this->parseClass($v2);
break;
case 'Stmt_Const':
$this->parseConstDef($v2) . PHP_EOL;
break;
case 'Stmt_Function':
$code .= $this->parseFunctionDef($v2) . PHP_EOL;
$code .= $this->parseFunction($v2) . PHP_EOL;
break;
case 'Stmt_Use':
$code .= $this->parseUse($v2) . PHP_EOL;
@ -1723,33 +1726,6 @@ class CompilerBase extends \PhpAot\Core\Translator
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
{
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)) {
return '_php__var__' . $name;
} elseif ($name === 'this') {
return 'this_';
} else {
return $name;
}
@ -1843,6 +1821,11 @@ class CompilerBase extends \PhpAot\Core\Translator
return str_replace('\\', self::NAMESPACE_SEPARATOR, strtolower($ns));
}
protected function escapeClass(string $class): string
{
return $class;
}
protected function unescapeVarName(string $name): string
{
return str_replace('_php__var__', '', $name);
@ -2022,7 +2005,7 @@ class CompilerBase extends \PhpAot\Core\Translator
shell_exec($cmd);
}
protected function genTmpVarName(): string
public function genTmpVarName(): string
{
return 'tmp_var_' . $this->tmpVarIndex++;
}
@ -2182,18 +2165,6 @@ class CompilerBase extends \PhpAot\Core\Translator
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
{
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
{
return $this->buildDir;
@ -2552,28 +2528,4 @@ class CompilerBase extends \PhpAot\Core\Translator
}
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;
class ConstDef
class ConstantDef
{
public string $name;
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);
break;
case 'Stmt_Class':
$this->prepareClassDef($v);
$this->prepareClass($v);
break;
case 'Stmt_Function':
$this->prepareFunctionDef($v) . PHP_EOL;
$this->prepareFunction($v) . PHP_EOL;
break;
case 'Stmt_Declare':
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);
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->namespace = $this->escapeNamespace($this->parseIdentifier($node->name));
@ -92,10 +92,10 @@ class Preprocessor extends CompilerBase
$type2 = $v2->getType();
switch ($type2) {
case 'Stmt_Class':
$this->prepareClassDef($v2);
$this->prepareClass($v2);
break;
case 'Stmt_Function':
$this->prepareFunctionDef($v2) . PHP_EOL;
$this->prepareFunction($v2) . PHP_EOL;
break;
case 'Stmt_Use':
case 'Stmt_Const':
@ -107,18 +107,18 @@ class Preprocessor extends CompilerBase
$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 = '';
foreach ($v->stmts as $v) {
foreach ($class->stmts as $v) {
$type = $v->getType();
switch ($type) {
case 'Stmt_ClassConst':
case 'Stmt_Property':
break;
case 'Stmt_ClassMethod':
$code .= $this->prepareFunctionDef($v) . PHP_EOL;
$code .= $this->prepareFunction($v) . PHP_EOL;
break;
default:
abort($v);

@ -2,6 +2,8 @@
namespace PhpAot\Php;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\NodeTraverser;
class Translator extends Preprocessor
@ -112,8 +114,7 @@ class Translator extends Preprocessor
$stmts = $traverser->traverse($ast);
$this->indentLevel = 0;
$this->strictTypes = false;
$this->resetFile();
$this->resetNamespace();
$cppCode = '';
@ -127,13 +128,13 @@ class Translator extends Preprocessor
$cppCode .= $this->parseNamespaceDef($v);
break;
case 'Stmt_Class':
$cppCode .= $this->parseClassDef($v);
$cppCode .= $this->parseClass($v);
break;
case 'Stmt_Use':
$cppCode .= $this->parseUse($v) . PHP_EOL;
break;
case 'Stmt_Function':
$cppCode .= $this->parseFunctionDef($v) . PHP_EOL;
$cppCode .= $this->parseFunction($v) . PHP_EOL;
break;
case 'Stmt_Const':
$this->parseConstDef($v) . PHP_EOL;
@ -143,7 +144,7 @@ class Translator extends Preprocessor
}
}
// include + extern global vars + function impl
return $this->genIncludeHeaderFiles() . $this->genExternGlobalVars() . $cppCode;
return $this->genIncludeHeaderFiles() . $cppCode;
}
public function preprocessArgvAdvanced(): void
@ -170,6 +171,20 @@ class Translator extends Preprocessor
$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
{
$code = $this->genIncludeHeaderFiles();
@ -260,7 +275,7 @@ class Translator extends Preprocessor
public function genFunctionDeclaration(string $file): void
{
$code = '';
$code = '#include <phpx.h>' . PHP_EOL;
/**
* @var FunctionDef $func
*/
@ -288,4 +303,239 @@ class Translator extends Preprocessor
$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