Skip to content

Commit

Permalink
Added PyClass, Support Python class as parent for PHP
Browse files Browse the repository at this point in the history
  • Loading branch information
matyhtf committed Sep 5, 2024
1 parent b1b79b7 commit 17ff71c
Show file tree
Hide file tree
Showing 9 changed files with 154 additions and 3 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Makefile
/CMakeFiles
/.settings
__pycache__
__phpy_proxy__
/vendor
/venv
/.pytest_cache
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ echo $os->uname();
### Transformers

```php
$transformers = PyCore::import( 'transformers');
$transformers = PyCore::import('transformers');
$AutoTokenizer = $transformers->AutoTokenizer;
$AutoModelForSequenceClassification = $transformers->AutoModelForSequenceClassification;

Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"autoload": {
"psr-4": {
"python\\": "./lib/python",
"PhpyTool\\": "tools/src"
"PhpyTool\\": "tools/src",
"phpy\\": "./lib/phpy"
}
},
"scripts": {
Expand Down
20 changes: 20 additions & 0 deletions examples/class/Dog.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

use phpy\PyClass;


#[parent('Animal', 'animal')]
class Dog extends PyClass
{
function __construct(string $name, int $age)
{
parent::__construct($name, $age);
$this->self()->color = 'black';
}

function speak(string $name): void
{
echo "Dog $name, color: {$this->self()->color}, speak: wang wang wang\n";
$this->super()->speak('dog');
}
}
2 changes: 1 addition & 1 deletion examples/class/animal.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ def __init__(self, name, age):
self.age = age

def speak(self, name):
print("Animal speak")
print("Animal speak, Age: ", self.age)
pass
File renamed without changes.
9 changes: 9 additions & 0 deletions examples/class/proxy_class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php
require dirname(__DIR__, 2) . '/vendor/autoload.php';
require dirname(__DIR__, 2) . '/lib/phpy/PyClass.php';
require __DIR__ . '/Dog.php';

PyCore::import('sys')->path->append('.');

$dog = new Dog('dog', 1);
$dog->speak('hello');
107 changes: 107 additions & 0 deletions lib/phpy/PyClass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php

namespace phpy;

use PyCore;
use PyObject;
use ReflectionClass;
use ReflectionMethod;

class PyClass
{
static private string $_proxyPath = '';
static private ?PyObject $_sys = null;
protected ?PyObject $_self = null;
protected ?PyObject $_super = null;
protected string $_proxyClass;
private string $_proxyFile;
private string $_class;

function __construct()
{
if (!self::$_sys) {
self::$_sys = PyCore::import('sys');
}
if (!self::$_proxyPath) {
self::setProxyPath(getcwd() . '/__phpy_proxy__');
}

$this->_class = get_class($this);
$this->_proxyClass = strtolower(str_replace('\\', '_', $this->_class));
$this->_proxyFile = self::$_proxyPath . '/' . $this->_proxyClass . '.py';

if (!is_file($this->_proxyFile)) {
$this->makeProxy();
}
PyCore::import($this->_proxyClass)->{$this->_proxyClass}($this);
if (func_num_args() > 0) {
$this->super()->__init__(...func_get_args());
}
}

private function makeProxy()
{
$ref = new ReflectionClass($this);
$refParents = $ref->getAttributes('parent');

$import = '';
$parents = [];

if (empty($refParents)) {
$parents[] = 'object';
} else {
foreach ($refParents as $refParent) {
$parent = $refParent->getArguments();
if (count($parent) == 1) {
$parents[] = $parent[0];
} else {
[$class, $package] = $parent;
$import .= 'import ' . $package . PHP_EOL;
$parents[] = $package . '.' . $class;
}
}
}

$refMethods = $ref->getMethods(ReflectionMethod::IS_PUBLIC);
$methods = [];
foreach ($refMethods as $method) {
$modifiers = $method->getModifiers();
if (str_starts_with($method->name, '__') or $modifiers & ReflectionMethod::IS_STATIC) {
continue;
}
$refParams = $method->getParameters();
$params = [];
foreach ($refParams as $param) {
$params[] = $param->name;
}
$methods[] = [
'name' => $method->name,
'argv' => implode(', ', $params),
];
}
ob_start();
include __DIR__ . '/proxy.tpl';
$content = ob_get_clean();
$dir = dirname($this->_proxyFile);
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
file_put_contents($this->_proxyFile, $content);
}

protected function super()
{
return $this->_super;
}

protected function self()
{
return $this->_self;
}

static function setProxyPath(string $path): void
{
self::$_proxyPath = $path;
self::$_sys->path->append($path);
}
}
13 changes: 13 additions & 0 deletions lib/phpy/proxy.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?=$import?>

class <?=$this->_proxyClass?>(<?=implode(', ', $parents)?>):
def __init__(self, _this):
self.__this = _this
_this.set('_super', super())
_this.set('_self', self)

<?php foreach($methods as $m):?>
def <?=$m['name']?>(self, <?=$m['argv']?>):
self.__this.call('<?=$m['name']?>', <?=$m['argv']?>)

<?php endforeach; ?>

0 comments on commit 17ff71c

Please sign in to comment.