From a536172f822b7466b054337013a68c383051bc40 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 13 Aug 2026 12:32:59 +0800 Subject: [PATCH] feat(python): implement native bridge for python object calls and property access - Add callDynamicPythonMethod function for dynamic python method invocation - Implement parsePythonObjectCall for handling python object function calls - Add parsePythonNativeFacadeMethodCall for native facade method calls - Implement parsePythonObjectPropertyFetch for property access translation - Replace .call() and .attr() syntax with native php::python::callMember and getAttr - Add native constructors mapping for python builtins like list, dict, tuple - Update test cases to verify native bridge usage instead of zend dispatch - Add python runtime configuration functions using native bridge - Integrate phpx_python.h header for python native bridge support --- phpunit/code/python/dynamic-method.php | 6 + phpunit/src/Python/PythonModuleTest.php | 86 +++++++++---- src/Parser/FunctionCallTrait.php | 4 + src/Parser/MethodCallTrait.php | 19 +++ src/Parser/PropertyAccessTrait.php | 5 + src/Python/PythonModuleTrait.php | 156 ++++++++++++++++++------ src/Translator.php | 1 + 7 files changed, 213 insertions(+), 64 deletions(-) create mode 100644 phpunit/code/python/dynamic-method.php diff --git a/phpunit/code/python/dynamic-method.php b/phpunit/code/python/dynamic-method.php new file mode 100644 index 00000000..49b7d2b4 --- /dev/null +++ b/phpunit/code/python/dynamic-method.php @@ -0,0 +1,6 @@ +$method(1); +} diff --git a/phpunit/src/Python/PythonModuleTest.php b/phpunit/src/Python/PythonModuleTest.php index 9d1118a6..00249631 100644 --- a/phpunit/src/Python/PythonModuleTest.php +++ b/phpunit/src/Python/PythonModuleTest.php @@ -9,7 +9,39 @@ use TypePhp\Exception\TestError; final class PythonModuleTest extends TestCase { - public function testUsedModuleGeneratesLazyZendBindingWithoutPhpyLinkage(): void + public function testStaticallyResolvedPythonCallsUseNativeBridge(): void + { + global $translator; + $compiler = CompilerTest::create(ROOT_PATH); + $translator = $compiler; + $source = ROOT_PATH . '/phpunit/code/python/module-access.php'; + $compiler->addFiles([$source]); + $compiler->prepareFile($source); + $cpp = file_get_contents($compiler->convertFile($source)); + $extension = file_get_contents($compiler->genExtension()); + + self::assertStringContainsString('php::python::callMember(', $cpp); + self::assertStringContainsString('php::python::getAttr(', $cpp); + self::assertStringContainsString('#include ', $cpp); + self::assertStringContainsString('php::python::importModule(', $extension); + self::assertStringNotContainsString('php::call(', $extension); + } + + public function testDynamicPythonMethodNameStillUsesZendDispatch(): void + { + global $translator; + $compiler = CompilerTest::create(ROOT_PATH); + $translator = $compiler; + $source = ROOT_PATH . '/phpunit/code/python/dynamic-method.php'; + $compiler->addFiles([$source]); + $compiler->prepareFile($source); + $cpp = file_get_contents($compiler->convertFile($source)); + + self::assertStringContainsString('.call(', $cpp); + self::assertStringNotContainsString('php::python::callMember(', $cpp); + } + + public function testUsedModuleGeneratesLazyNativeBindingWithoutPhpyLinkage(): void { global $translator; $compiler = CompilerTest::create(ROOT_PATH); @@ -23,13 +55,12 @@ final class PythonModuleTest extends TestCase $extension = file_get_contents($extensionFile); $this->assertStringContainsString('php_get_python_module(', $cpp); - $this->assertStringContainsString('.attr(', $cpp); - $this->assertStringContainsString('.call(', $cpp); + $this->assertStringContainsString('php::python::getAttr(', $cpp); + $this->assertStringContainsString('php::python::callMember(', $cpp); $this->assertStringContainsString('THREAD_LOCAL zval php_python_module_map[1]', $extension); $this->assertStringContainsString('php::Object php_get_python_module(', $extension); $this->assertStringContainsString('zval_ptr_dtor(', $extension); - $this->assertStringContainsString('PyCore', $extension); - $this->assertStringContainsString('import', $extension); + $this->assertStringContainsString('php::python::importModule(', $extension); $this->assertStringNotContainsString('#include assertStringNotContainsString('phpy::', $extension); $this->assertStringNotContainsString('module_that_does_not_exist', $extension); @@ -71,7 +102,7 @@ final class PythonModuleTest extends TestCase $cpp = file_get_contents($compiler->convertFile($source)); $this->assertStringContainsString('php_get_python_module(', $cpp); - $this->assertStringContainsString('.attr(', $cpp); + $this->assertStringContainsString('php::python::getAttr(', $cpp); } public function testModuleStaticCallSyntaxIsRejected(): void @@ -125,8 +156,8 @@ final class PythonModuleTest extends TestCase $extension = file_get_contents($compiler->genExtension()); $this->assertSame(4, substr_count($cpp, 'php_get_python_module(')); - $this->assertStringContainsString('.call(', $cpp); - $this->assertStringContainsString('.attr(', $cpp); + $this->assertStringContainsString('php::python::callMember(', $cpp); + $this->assertStringContainsString('php::python::getAttr(', $cpp); $this->assertStringContainsString('builtins', $extension); $this->assertStringContainsString('math', $extension); $this->assertStringContainsString('THREAD_LOCAL zval php_python_module_map[2]', $extension); @@ -143,8 +174,8 @@ final class PythonModuleTest extends TestCase $cpp = file_get_contents($compiler->convertFile($source)); $extension = file_get_contents($compiler->genExtension()); - $this->assertStringContainsString('.call(', $cpp); - $this->assertStringContainsString('.attr(', $cpp); + $this->assertStringContainsString('php::python::callMember(', $cpp); + $this->assertStringContainsString('php::python::getAttr(', $cpp); $this->assertStringContainsString('math', $extension); $this->assertStringContainsString('os.path', $extension); $this->assertStringContainsString('builtins', $extension); @@ -201,7 +232,7 @@ final class PythonModuleTest extends TestCase $extension = file_get_contents($compiler->genExtension()); $this->assertStringContainsString('php_get_python_module(', $cpp); - $this->assertStringContainsString('.call(', $cpp); + $this->assertStringContainsString('php::python::callMember(', $cpp); $this->assertStringContainsString('php::Object list;', $cpp); $this->assertStringContainsString('php::Object dict;', $cpp); $this->assertStringContainsString('php::Object tuple;', $cpp); @@ -211,14 +242,15 @@ final class PythonModuleTest extends TestCase $this->assertStringContainsString('php::Object object;', $cpp); $this->assertStringContainsString('php::Object bytes;', $cpp); $this->assertStringContainsString('scalar = php::toInt(', $cpp); - $this->assertStringContainsString('php::newObject(', $cpp); - $this->assertStringContainsString('PyList', $extension); - $this->assertStringContainsString('PyDict', $extension); + $this->assertSame(8, substr_count($cpp, 'php::python::construct(')); + $this->assertStringNotContainsString('php::newObject(', $cpp); + $this->assertStringNotContainsString('php::call(php_get_persistent_class(_literal_strings', $cpp); + $this->assertStringNotContainsString('PyList', $extension); + $this->assertStringNotContainsString('PyDict', $extension); $this->assertStringContainsString('THREAD_LOCAL zval php_python_module_map[1]', $extension); $this->assertStringContainsString('builtins', $extension); - $this->assertStringContainsString('php_get_persistent_method(', $extension); - $this->assertStringContainsString('setOptions', $extension); - $this->assertStringContainsString('return_as_object', $extension); + $this->assertStringNotContainsString('PyCore::int', $extension); + $this->assertStringContainsString('php::python::configureRuntime(true)', $extension); $this->assertStringContainsString('php_python_runtime_configured = false;', $extension); $this->assertStringNotContainsString('python\\\\list', $extension); } @@ -234,7 +266,7 @@ final class PythonModuleTest extends TestCase $cpp = file_get_contents($compiler->convertFile($source)); $extension = file_get_contents($compiler->genExtension()); - $this->assertStringContainsString('.call(', $cpp); + $this->assertStringContainsString('php::python::callMember(', $cpp); $this->assertStringContainsString('collections', $extension); $this->assertStringContainsString('deque', $extension); } @@ -252,7 +284,7 @@ final class PythonModuleTest extends TestCase $this->assertStringContainsString('php_configure_python_runtime()', $cpp); $this->assertStringContainsString('void php_configure_python_runtime()', $extension); - $this->assertStringContainsString('return_as_object', $extension); + $this->assertStringContainsString('php::python::configureRuntime(true)', $extension); } public function testPythonOperatorsLowerToTheOperatorModule(): void @@ -267,14 +299,14 @@ final class PythonModuleTest extends TestCase $extension = file_get_contents($compiler->genExtension()); $this->assertStringContainsString('operator', $extension); - $this->assertStringContainsString('.call(', $cpp); + $this->assertStringContainsString('php::python::callMember(', $cpp); $this->assertStringContainsString('add', $extension); $this->assertStringContainsString('iadd', $extension); $this->assertStringContainsString('is_', $extension); $this->assertStringContainsString('is_not', $extension); } - public function testPythonObjectProtocolStaysOnZendDynamicDispatch(): void + public function testPythonObjectProtocolUsesNativeCallsAndZendHandlersWhereAppropriate(): void { global $translator; $compiler = CompilerTest::create(ROOT_PATH); @@ -285,8 +317,10 @@ final class PythonModuleTest extends TestCase $cpp = file_get_contents($compiler->convertFile($source)); $extension = file_get_contents($compiler->genExtension()); - $this->assertStringContainsString('.call(', $cpp); - $this->assertStringContainsString('.attr(', $cpp); + $this->assertStringContainsString('php::python::callMember(', $cpp); + $this->assertStringContainsString('php::python::call(', $cpp); + $this->assertStringContainsString('php::python::getAttr(', $cpp); + $this->assertStringNotContainsString('.attr(', $cpp); $this->assertStringContainsString('.item(', $cpp); $this->assertStringContainsString('php::ForeachIterator', $cpp); $this->assertStringContainsString('integer = php::toInt(object);', $cpp); @@ -294,7 +328,7 @@ final class PythonModuleTest extends TestCase $this->assertStringNotContainsString('phpy::', $cpp); } - public function testPyObjectConversionMethodsUseThePhpyFacade(): void + public function testPyObjectConversionMethodsUseTheNativeBridge(): void { global $translator; $compiler = CompilerTest::create(ROOT_PATH); @@ -308,8 +342,8 @@ final class PythonModuleTest extends TestCase $this->assertStringContainsString('php::Array array;', $cpp); $this->assertStringContainsString('php::Var value;', $cpp); $this->assertStringNotContainsString('php::toArray(', $cpp); - $this->assertStringContainsString('toArray', $extension); - $this->assertStringContainsString('toValue', $extension); + $this->assertStringContainsString('php::python::toArray(', $cpp); + $this->assertStringContainsString('php::python::toValue(', $cpp); $this->assertStringNotContainsString('php::toPlainValue(', $cpp); $this->assertStringNotContainsString('phpy::', $cpp); } diff --git a/src/Parser/FunctionCallTrait.php b/src/Parser/FunctionCallTrait.php index 0d22826e..76291ad6 100644 --- a/src/Parser/FunctionCallTrait.php +++ b/src/Parser/FunctionCallTrait.php @@ -75,6 +75,10 @@ trait FunctionCallTrait if ($pythonCall !== null) { return $pythonCall; } + $pythonObjectCall = $this->parsePythonObjectCall($expr); + if ($pythonObjectCall !== null) { + return $pythonObjectCall; + } if ($this->isVarExpr($expr->name)) { $fn = $this->parseIdentifier($expr->name); diff --git a/src/Parser/MethodCallTrait.php b/src/Parser/MethodCallTrait.php index 92417ce8..d3fd56d3 100644 --- a/src/Parser/MethodCallTrait.php +++ b/src/Parser/MethodCallTrait.php @@ -382,6 +382,11 @@ trait MethodCallTrait $magicMethod = false; $method = $this->identifierToStr($expr->name, literal: true); + $pythonFacadeCall = $this->parsePythonNativeFacadeMethodCall($expr, $object); + if ($pythonFacadeCall !== null) { + return $pythonFacadeCall; + } + // Keyword methods are dispatched before all receiver-specific logic. if ($this->isNamedMethod($expr->name)) { $methodName = $expr->name->toString(); @@ -428,6 +433,20 @@ trait MethodCallTrait } } + // A statically named Python member can bypass PyObject::__call and + // zend_call_function(). Variable method names remain fully dynamic and + // intentionally continue through ZendVM. + if ($this->isNamedMethod($expr->name) + && $this->isPythonDynamicMethodCall($expr->var, $expr->name->toString()) + ) { + $name = $this->getLiteralString($expr->name->toString()); + if ($expr->args === []) { + return 'php::python::callMember(' . $object . ', ' . $name . ')'; + } + return 'php::python::callMember(' . $object . ', ' . $name . ', ' + . $this->parseCallArgs($expr->args) . ')'; + } + // 可转为原生调用的 MethodCall if ($this->isVarExpr($expr->var) and $this->isNamedMethod($expr->name)) { $type = $this->getVarType($object); diff --git a/src/Parser/PropertyAccessTrait.php b/src/Parser/PropertyAccessTrait.php index cd3a5a86..24274944 100644 --- a/src/Parser/PropertyAccessTrait.php +++ b/src/Parser/PropertyAccessTrait.php @@ -918,6 +918,11 @@ trait PropertyAccessTrait return $this->parseNullsafeExpr($expr); } + $pythonProperty = $this->parsePythonObjectPropertyFetch($expr); + if ($pythonProperty !== null) { + return $pythonProperty; + } + $object = $expr->var; $property = $expr->name; $id = $this->getPropertyIdentifier($expr, $object, $property); diff --git a/src/Python/PythonModuleTrait.php b/src/Python/PythonModuleTrait.php index 0357203c..cd2a3dd6 100644 --- a/src/Python/PythonModuleTrait.php +++ b/src/Python/PythonModuleTrait.php @@ -36,6 +36,19 @@ trait PythonModuleTrait 'scalar' => true, ]; + /** @var array Builtins with a direct phpy Native ABI constructor. */ + private const PYTHON_NATIVE_CONSTRUCTORS = [ + 'list' => 'List', + 'dict' => 'Dict', + 'tuple' => 'Tuple', + 'set' => 'Set', + 'str' => 'Str', + 'object' => 'Object', + 'int' => 'Int', + 'float' => 'Float', + 'bytes' => 'Bytes', + ]; + /** @var array Case-sensitive Python module name to generated slot ID. */ protected array $pythonModuleMap = []; @@ -69,6 +82,56 @@ trait PythonModuleTrait return $class === '' || !\TypePhp\Resolver\Reflection::hasMethod($class, $method); } + protected function parsePythonNativeFacadeMethodCall(Expr\MethodCall $expr, string $receiver): ?string + { + if (!$this->isNamedMethod($expr->name) || !$this->isPythonObjectExpr($expr->var)) { + return null; + } + $method = $expr->name->toString(); + $helper = match ($method) { + 'toValue' => 'toValue', + 'toArray' => 'toArray', + default => null, + }; + if ($helper === null) { + return null; + } + if ($expr->args !== []) { + $this->fatalError($expr, "The {$method} method does not accept parameters"); + } + $this->markPythonRuntimeUsed(); + return 'php::python::' . $helper . '(' . $receiver . ')'; + } + + protected function parsePythonObjectCall(Expr\FuncCall $expr): ?string + { + if (!$expr->name instanceof NodeAbstract || !$this->isPythonObjectExpr($expr->name)) { + return null; + } + if ($expr->isFirstClassCallable()) { + $this->fatalError($expr, 'Python objects do not support first-class callable syntax yet'); + } + + $receiver = $this->addTmpVar(Type::VAR); + $this->context->beforeStmtLines[] = $receiver . ' = ' . $this->parseExpr($expr->name) . ';'; + if ($expr->args === []) { + return 'php::python::call(' . $receiver . ')'; + } + return 'php::python::call(' . $receiver . ', ' . $this->parseCallArgs($expr->args) . ')'; + } + + protected function parsePythonObjectPropertyFetch(Expr\PropertyFetch $expr): ?string + { + if ($this->isPropertyFetchUpdate($expr) + || !$this->isIdExpr($expr->name) + || !$this->isPythonObjectExpr($expr->var)) { + return null; + } + + return 'php::python::getAttr(' . $this->parseIdentifier($expr->var) . ', ' + . $this->propertyNameToStr($expr->name, literal: true) . ')'; + } + protected function getPythonBinaryOperator(Expr\BinaryOp $expr): ?string { return match (true) { @@ -127,8 +190,8 @@ trait PythonModuleTrait $left = $this->parseOrderedOperand($expr->left, false); $right = $this->parseOrderedOperand($expr->right, false); - $call = $this->getPythonModuleExpression('operator') - . '.call(' . $this->getLiteralString($this->getPythonBinaryOperator($expr)) + $call = 'php::python::callMember(' . $this->getPythonModuleExpression('operator') + . ', ' . $this->getLiteralString($this->getPythonBinaryOperator($expr)) . ', php::ArgList{' . $left . ', ' . $right . '})'; return $this->pythonOperatorReturnsBool($expr) @@ -139,8 +202,7 @@ trait PythonModuleTrait protected function convertPythonResultToBool(string $expression): string { $this->markPythonRuntimeUsed(); - $scalar = 'php::call(' . $this->getClassEntryPtr('PyCore') . ', ' - . $this->getMethodPtr('PyCore', 'scalar') . ', php::ArgList{' . $expression . '})'; + $scalar = 'php::python::toValue(' . $expression . ')'; return 'php::toBool(' . $scalar . ')'; } @@ -190,8 +252,8 @@ trait PythonModuleTrait } $operand = $this->parseOrderedOperand($expr->expr, false); - $call = $this->getPythonModuleExpression('operator') - . '.call(' . $this->getLiteralString($this->getPythonUnaryOperator($expr)) + $call = 'php::python::callMember(' . $this->getPythonModuleExpression('operator') + . ', ' . $this->getLiteralString($this->getPythonUnaryOperator($expr)) . ', php::ArgList{' . $operand . '})'; return $expr instanceof Expr\BooleanNot ? $this->convertPythonResultToBool($call) : $call; } @@ -201,8 +263,8 @@ trait PythonModuleTrait if (!$this->isPythonObjectExpr($expr)) { return null; } - $call = $this->getPythonModuleExpression('operator') - . '.call(' . $this->getLiteralString('truth') . ', php::ArgList{' . $parsed . '})'; + $call = 'php::python::callMember(' . $this->getPythonModuleExpression('operator') + . ', ' . $this->getLiteralString('truth') . ', php::ArgList{' . $parsed . '})'; return $this->convertPythonResultToBool($call); } @@ -251,7 +313,7 @@ trait PythonModuleTrait } elseif ($expr->var instanceof Expr\PropertyFetch && $this->isIdExpr($expr->var->name)) { $receiver = $this->parseOrderedOperand($expr->var->var, false); $property = $this->propertyNameToStr($expr->var->name, literal: true); - $left = $receiver . '.attr(' . $property . ', php::AttrMode::Get)'; + $left = 'php::python::getAttr(' . $receiver . ', ' . $property . ')'; $writeBack = static fn(string $value): string => 'typephp_write_property_scoped(' . $receiver . ', ' . $property . ', ' . $value . ', nullptr)'; } else { @@ -259,8 +321,8 @@ trait PythonModuleTrait } $right = $this->parseOrderedOperand($expr->expr, false); - $call = $this->getPythonModuleExpression('operator') - . '.call(' . $this->getLiteralString($method) + $call = 'php::python::callMember(' . $this->getPythonModuleExpression('operator') + . ', ' . $this->getLiteralString($method) . ', php::ArgList{' . $left . ', ' . $right . '})'; if ($this->isVarExpr($expr->var)) { return $writeBack($call); @@ -329,8 +391,6 @@ trait PythonModuleTrait $this->pythonModuleMap[$module] = $id; $this->markPythonRuntimeUsed(); - $this->getFuncId('PyCore::import'); - $this->getLiteralString('import'); $this->getLiteralString($module); return $id; @@ -343,13 +403,6 @@ trait PythonModuleTrait } $this->pythonRuntimeUsed = true; - // Register runtime Zend symbols during conversion so generated map - // sizes are final before headers are emitted. - $this->getClassId('PyCore'); - $this->getFuncId('PyCore::setOptions'); - $this->getLiteralString('PyCore'); - $this->getLiteralString('setOptions'); - $this->getLiteralString('return_as_object'); } protected function withPythonRuntimeConfigured(string $expression): string @@ -412,9 +465,10 @@ trait PythonModuleTrait $target = $this->getPythonModuleExpression($moduleMember['module']); $member = $this->getLiteralString($moduleMember['member']); if ($expr->args === []) { - return $target . '.call(' . $member . ')'; + return 'php::python::callMember(' . $target . ', ' . $member . ')'; } - return $target . '.call(' . $member . ', ' . $this->parseCallArgs($expr->args) . ')'; + return 'php::python::callMember(' . $target . ', ' . $member . ', ' + . $this->parseCallArgs($expr->args) . ')'; } $builtin = $this->resolvePythonBuiltinName($expr->name); @@ -426,6 +480,23 @@ trait PythonModuleTrait } $this->markPythonRuntimeUsed(); + if ($builtin === 'scalar') { + if (count($expr->args) !== 1 || $expr->args[0]->name !== null || $expr->args[0]->unpack) { + $this->fatalError($expr, 'python\\scalar() expects exactly one positional argument'); + } + return 'php::python::toValue(' . $this->parseCallArgValue($expr->args[0]) . ')'; + } + + $nativeConstructor = self::PYTHON_NATIVE_CONSTRUCTORS[$builtin] ?? null; + if ($nativeConstructor !== null && $this->canUsePythonNativeConstructor($expr)) { + $constructor = 'php::python::Constructor::' . $nativeConstructor; + $call = $expr->args === [] + ? 'php::python::construct(' . $constructor . ')' + : 'php::python::construct(' . $constructor . ', ' + . $this->parseCallArgValue($expr->args[0]) . ')'; + return $this->withPythonRuntimeConfigured($call); + } + // This is a deliberately closed map. In particular, PyDict's PHP-array // constructor is not equivalent to Python's dict(iterable) builtin. $constructorClass = self::PYTHON_CONSTRUCTOR_CLASSES[$builtin] ?? null; @@ -456,9 +527,28 @@ trait PythonModuleTrait $target = $this->getPythonModuleExpression('builtins'); $name = $this->getLiteralString($builtin); if ($expr->args === []) { - return $target . '.call(' . $name . ')'; + return 'php::python::callMember(' . $target . ', ' . $name . ')'; } - return $target . '.call(' . $name . ', ' . $this->parseCallArgs($expr->args) . ')'; + return 'php::python::callMember(' . $target . ', ' . $name . ', ' + . $this->parseCallArgs($expr->args) . ')'; + } + + /** + * The Native ABI has a deliberately small zero/one positional argument + * constructor path. Keep named and unpacked calls on Zend so its regular + * argument validation and unpacking semantics remain unchanged. + */ + private function canUsePythonNativeConstructor(Expr\FuncCall $expr): bool + { + if (count($expr->args) > 1) { + return false; + } + foreach ($expr->args as $arg) { + if ($arg->name !== null || $arg->unpack) { + return false; + } + } + return true; } protected function parsePythonModuleAttributeFetch(Expr\ConstFetch $expr, bool $constantExpression): ?string @@ -471,8 +561,8 @@ trait PythonModuleTrait $this->fatalError($expr, 'Python module attributes cannot be used in constant expressions'); } - return $this->getPythonModuleExpression($moduleMember['module']) - . '.attr(' . $this->getLiteralString($moduleMember['member']) . ')'; + return 'php::python::getAttr(' . $this->getPythonModuleExpression($moduleMember['module']) + . ', ' . $this->getLiteralString($moduleMember['member']) . ')'; } protected function detectPythonExpressionReturnType(NodeAbstract $expr): ?string @@ -647,17 +737,11 @@ trait PythonModuleTrait return ''; } - $pyCoreClass = $this->getClassEntryPtr('PyCore'); - $setOptionsFunction = $this->getMethodPtr('PyCore', 'setOptions'); - $returnAsObject = $this->getLiteralString('return_as_object'); - $code = 'void ' . self::PREFIX . 'configure_python_runtime() {' . PHP_EOL . 'if (EXPECTED(' . self::PREFIX . 'python_runtime_configured)) {' . PHP_EOL . 'return;' . PHP_EOL . '}' . PHP_EOL - . 'php::Array options;' . PHP_EOL - . 'options.set(' . $returnAsObject . ', true);' . PHP_EOL - . 'php::call(' . $pyCoreClass . ', ' . $setOptionsFunction . ', php::ArgList{options});' . PHP_EOL + . 'php::python::configureRuntime(true);' . PHP_EOL . self::PREFIX . 'python_runtime_configured = true;' . PHP_EOL . '}' . PHP_EOL . PHP_EOL; @@ -665,17 +749,13 @@ trait PythonModuleTrait return $code; } - $importFunction = $this->getMethodPtr('PyCore', 'import'); - return $code . 'php::Object ' . self::PREFIX . 'get_python_module(int module_id, const php::Str &module_name) {' . PHP_EOL . self::PREFIX . 'configure_python_runtime();' . PHP_EOL . 'zval *module = &' . self::PREFIX . 'python_module_map[module_id];' . PHP_EOL . 'if (UNEXPECTED(Z_ISUNDEF_P(module))) {' . PHP_EOL - . 'auto ce = ' . $pyCoreClass . ';' . PHP_EOL - . 'auto fn = ' . $importFunction . ';' . PHP_EOL - . 'php::Variant imported = php::call(ce, fn, php::ArgList{module_name});' . PHP_EOL + . 'php::Variant imported = php::python::importModule(module_name);' . PHP_EOL . '(void) php::Object(imported);' . PHP_EOL . 'imported.moveTo(module);' . PHP_EOL . '}' . PHP_EOL diff --git a/src/Translator.php b/src/Translator.php index f2839e4e..4e763b87 100644 --- a/src/Translator.php +++ b/src/Translator.php @@ -88,6 +88,7 @@ class Translator extends Preprocessor 'phpx_big_int.h', 'phpx_big_float.h', 'phpx_decimal.h', + 'phpx_python.h', 'typephp_helper.h', 'typephp_fiber_generator.h', 'phpx_std.h',