diff --git a/bin/compiler.php b/bin/compiler.php index a0222beb..90e3f8b0 100755 --- a/bin/compiler.php +++ b/bin/compiler.php @@ -15,6 +15,7 @@ $code = $translator->convert($file); $info = pathinfo($file); $cppFile = $info['dirname'] . '/' . $info['filename'] . '.cc'; $translator->save($code, $cppFile); +$translator->genFunctionDeclaration("./php_func_decl.h"); $translator->compileFile($cppFile); $objectFile = $info['dirname'] . '/' . $info['filename'] . '.cc.o'; diff --git a/src/Php/ArgInfo.php b/src/Php/ArgInfo.php index da894647..0bb403f3 100644 --- a/src/Php/ArgInfo.php +++ b/src/Php/ArgInfo.php @@ -6,5 +6,5 @@ class ArgInfo { public string $name; public string $type; - public string $default; + public string $default = ''; } \ No newline at end of file diff --git a/src/Php/FunctionDef.php b/src/Php/FunctionDef.php index 72c1e2da..6c5a2ede 100644 --- a/src/Php/FunctionDef.php +++ b/src/Php/FunctionDef.php @@ -5,7 +5,8 @@ namespace PhpAot\Php; class FunctionDef { public string $name; - public array $arguments; + public array $argInfoList = []; + public string $params; public string $returnType; - public int $argumentCountRequired = 0; + public int $argCountRequired = 0; } \ No newline at end of file diff --git a/src/Php/Reflection.php b/src/Php/Reflection.php index 4be80fd6..d9f84aa6 100644 --- a/src/Php/Reflection.php +++ b/src/Php/Reflection.php @@ -11,7 +11,12 @@ class Reflection static function getFunction(string $fn) { if (!isset(self::$functions[$fn])) { - self::$functions[$fn] = new ReflectionFunction($fn); + try { + $ref = new ReflectionFunction($fn);; + } catch (\ReflectionException $e) { + return null; + } + self::$functions[$fn] = $ref; } return self::$functions[$fn]; } @@ -19,6 +24,9 @@ class Reflection static function getFunctionReturnType(string $fn): ?string { $func = self::getFunction($fn); + if (!$func) { + return null; + } $returnType = $func->getReturnType(); if (!$returnType) { return null; diff --git a/src/Php/Translator.php b/src/Php/Translator.php index 0bba7e20..197609cf 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -40,9 +40,49 @@ class Translator extends \PhpAot\Core\Translator private array $headers = [ 'phpx.h', - 'phpx_func.h' + 'phpx_func.h', + 'php_func_decl.h', ]; + private array $reservedNames = [ + 'auto', + 'break', + 'case', + 'catch', + 'class', + 'struct', + 'const', + 'continue', + 'default', + 'do', + 'else', + 'elseif', + 'enum', + 'extends', + 'final', + 'finally', + 'for', + 'function', + 'global', + 'if', + 'int', + 'double', + 'float', + 'false', + 'for', + 'if', + 'int', + 'new', + 'null', + 'or', + 'and', + 'private', + 'protected', + 'public', + 'return', + 'static', + ]; + private array $internalFunctions = []; private int $optimizeLevel = 0; @@ -315,15 +355,17 @@ class Translator extends \PhpAot\Core\Translator } else { $this->functionDef->returnType = 'void'; } + $this->parseParams($v->params); $this->internalFunctions[$name] = $this->functionDef; } - $params = $this->parseParams($v->params); $this->indentLevel++; $stmts = $this->parseStmts($v->stmts); $this->indentLevel--; - $code = $this->getReturnType() . ' ' . self::PREFIX . $name . '(' . $params . ') {' . PHP_EOL; + $functionDeclCode = $this->getReturnType() . ' ' . self::PREFIX . $name . '(' . $this->functionDef->params . ')'; + + $code = $functionDeclCode . ' {' . PHP_EOL; $this->indentLevel++; foreach ($this->localVars as $name => $type) { if (isset($this->arguments[$name])) { @@ -358,7 +400,7 @@ class Translator extends \PhpAot\Core\Translator case 'Name': case 'Identifier': case 'Expr_Variable': - return $expr->name; + return $this->escapeVarName($expr->name); case 'Scalar_Int': return $expr->value . 'L'; case 'Scalar_Float': @@ -372,10 +414,10 @@ class Translator extends \PhpAot\Core\Translator } } - private function parseParams($params): string + private function parseParams($params): void { $list = []; - $this->functionDef->argumentCountRequired = count($params); + $this->functionDef->argCountRequired = count($params); foreach ($params as $param) { $type = $this->parseType($param->type); $name = $this->parseIdentifier($param->var); @@ -387,12 +429,12 @@ class Translator extends \PhpAot\Core\Translator $argInfo->name = $name; $argInfo->type = $type; if (isset($param->default)) { - $this->functionDef->argumentCountRequired = count($list) - 1; + $this->functionDef->argCountRequired = count($list) - 1; $argInfo->default = $param->default->getAttribute('rawValue'); } - $this->functionDef->arguments[] = $argInfo; + $this->functionDef->argInfoList[] = $argInfo; } - return implode(', ', $list); + $this->functionDef->params = implode(', ', $list); } private function parseStmts(array $stmts): string @@ -905,6 +947,7 @@ class Translator extends \PhpAot\Core\Translator { $list = [ $this->phpxDir . '/include', + './', ]; $out = '$(php-config --includes) '; foreach ($list as $li) { @@ -941,6 +984,7 @@ class Translator extends \PhpAot\Core\Translator private function addCompilationOption(string &$cmd): void { + $cmd .= $this->parseIncludes(); $cmd .= ' -O' . $this->optimizeLevel; $cmd .= ' -g'; if ($this->climate->arguments->defined('profile')) { @@ -951,7 +995,7 @@ class Translator extends \PhpAot\Core\Translator public function compileFile($file): void { - $cmd = $this->cppCompiler . ' -c ' . $file . ' -o ' . $file . '.o ' . $this->parseIncludes(); + $cmd = $this->cppCompiler . ' -c ' . $file . ' -o ' . $file . '.o '; $this->addCompilationOption($cmd); $this->climate->comment($cmd); shell_exec($cmd); @@ -963,7 +1007,7 @@ class Translator extends \PhpAot\Core\Translator if ($this->climate->arguments->defined('output')) { $targetFile = $this->climate->arguments->get('output'); } - $cmd = $this->cppCompiler . ' main.cc global_vars.cc ' . $objectFile . ' -o ' . $targetFile . ' ' . $this->parseIncludes() . $this->parseLdflags() . $this->parseLibs(); + $cmd = $this->cppCompiler . ' main.cc global_vars.cc ' . $objectFile . ' -o ' . $targetFile . ' ' . $this->parseLdflags() . $this->parseLibs(); $this->addCompilationOption($cmd); $this->climate->comment($cmd); shell_exec($cmd); @@ -1518,6 +1562,15 @@ class Translator extends \PhpAot\Core\Translator return addcslashes($str, "\\\"\n\r\t\v\f\0\x01..\x1f\x7f..\xff"); } + private function escapeVarName(string $name): string + { + if (in_array($name, $this->reservedNames)) { + return '_php__var__' . $name; + } else { + return $name; + } + } + private function parseInterpolatedStringPart(Node $expr): string { return '"' . $this->escapeString($expr->value) . '"'; @@ -1536,7 +1589,7 @@ class Translator extends \PhpAot\Core\Translator private function getArgInfo(string $funcName, int $index): ArgInfo { $funcDef = $this->internalFunctions[$funcName]; - return $funcDef->arguments[$index]; + return $funcDef->argInfoList[$index]; } private function getReturnType(): string @@ -1857,4 +1910,29 @@ class Translator extends \PhpAot\Core\Translator } return $expr; } + + public function genFunctionDeclaration(string $file): void + { + $code = ''; + /** + * @var FunctionDef $func + */ + foreach ($this->internalFunctions as $name => $func) { + $code .= 'extern ' . $func->returnType . ' ' . self::PREFIX . $name . '('; + $argInfoList = $func->argInfoList; + if ($argInfoList) { + $list = []; + foreach ($argInfoList as $argInfo) { + $arg = $argInfo->type . ' ' . $argInfo->name; + if ($argInfo->default) { + $arg .= ' = ' . $argInfo->default; + } + $list[] = $arg; + } + $code .= implode(', ', $list); + } + $code .= ');' . PHP_EOL; + } + file_put_contents($file, $code); + } } diff --git a/tests/aot/functions.phpt b/tests/aot/functions.phpt new file mode 100644 index 00000000..a9c5f088 --- /dev/null +++ b/tests/aot/functions.phpt @@ -0,0 +1,93 @@ +--TEST-- +User-defined and Built-in Functions +--FILE-- + +--EXPECT-- +int(8) +int(28) +string(13) "Hello, World!" +string(11) "Hello World" +string(15) " HELLO WORLD " +int(11) +array(5) { + [0]=> + int(5) + [1]=> + int(4) + [2]=> + int(3) + [3]=> + int(2) + [4]=> + int(1) +} +int(5) +int(120) +string(9) "Mr. Smith" +string(8) "Ms. Jane" \ No newline at end of file diff --git a/tests/aot/math_functions.phpt b/tests/aot/math_functions.phpt new file mode 100644 index 00000000..02b02e64 --- /dev/null +++ b/tests/aot/math_functions.phpt @@ -0,0 +1,140 @@ +--TEST-- +Mathematical Functions +--FILE-- + +--EXPECT-- +int(5) +float(3.7) +float(5) +float(4) +float(4.57) +int(1) +int(9) +int(1) +int(9) +int(256) +float(8) +float(1.4142135623730951) +int(91) +int(91) +string(8) "1,234.57" +float(0.8999999999999995) +float(1) +float(1) +float(0.9999999999999999) +float(1) +float(2) +float(2.718281828459045) +bool(true) +bool(true) +bool(true) +bool(false) +string(2) "ff" +string(4) "1000" +string(2) "10" +int(255) diff --git a/tests/aot/string_functions.phpt b/tests/aot/string_functions.phpt new file mode 100644 index 00000000..124a02e6 --- /dev/null +++ b/tests/aot/string_functions.phpt @@ -0,0 +1,156 @@ +--TEST-- +String Functions +--FILE-- +Hello World!

"; +$stripped = strip_tags($html); +var_dump($stripped); +?> +--EXPECT-- +int(13) +string(13) "HELLO, WORLD!" +string(13) "hello, world!" +string(5) "Hello" +string(6) "World!" +int(7) +string(11) "Hello, PHP!" +string(13) "Hello, World!" +string(15) "Hello, World! " +string(15) " Hello, World!" +string(12) "Hello World!" +array(3) { + [0]=> + string(5) "apple" + [1]=> + string(6) "banana" + [2]=> + string(6) "orange" +} +string(19) "apple-banana-orange" +int(0) +int(-3) +int(0) +string(22) "Value: 42, Text: hello" +array(4) { + [0]=> + string(3) "abc" + [1]=> + string(3) "def" + [2]=> + string(3) "ghi" + [3]=> + string(1) "j" +} +string(5) "olleh" +int(2) +string(20) "Hello world from PHP" +string(20) "Hello World From PHP" +int(123) +float(45.67) +string(10) "00000Hello" +string(8) "xyxyxyxy" +string(12) "Hello World!"