diff --git a/docs/en/.translate.json b/docs/en/.translate.json new file mode 100644 index 0000000..7603b24 --- /dev/null +++ b/docs/en/.translate.json @@ -0,0 +1 @@ +{"latest_commit":"8e5bc8856cde91b53e08f20728b3edceafb79f07"} \ No newline at end of file diff --git a/docs/en/php/build-on-windows.md b/docs/en/php/build-on-windows.md new file mode 100644 index 0000000..e8e7b8e --- /dev/null +++ b/docs/en/php/build-on-windows.md @@ -0,0 +1,138 @@ +# Windows Compilation + +> Only supports `PHP-8.1` or higher + + +## PHP Development Environment + +Refer to: [https://wiki.php.net/internals/windows/stepbystepbuild_sdk_2](https://wiki.php.net/internals/windows/stepbystepbuild_sdk_2) + + + +- Install `Visual C++ 16.0`, address: [https://aka.ms/vs/16/release/vs_buildtools.exe](https://aka.ms/vs/16/release/vs_buildtools.exe) + +- Install `php-sdk-binary-tools`, address: [https://github.com/php/php-sdk-binary-tools](https://github.com/php/php-sdk-binary-tools) + +- Download `php` source code +- Install dependency libraries, address: [https://windows.php.net/downloads/php-sdk/deps/](https://windows.php.net/downloads/php-sdk/deps/) + +> All files related to `PHP` are installed in the `d:\workspace` directory + + +## Python Development Environment + + + +- Install `Python`, address: [https://www.python.org/downloads/windows/](https://www.python.org/downloads/windows/) + +- Set `Path` environment variable, add `d:\python` to the list, execute `Python -V` in `Windows Terminal` +- Set `PYTHONHOME` environment variable, pointing to `d:\python` + +> `Python` is installed in the `d:\python` directory + +```shell +C:\WINDOWS\system32>python -V +Python 3.12.1 + +echo %Path% +echo %PYTHONHOME% +``` + + + +## Build Directory + +```shell +cd D:\workspace\php-sdk\php-sdk-binary-tools-2.2.0 +phpsdk-vs16-x64.bat +``` + +After successful execution, enter the `php-src` directory in this terminal + +```shell +cd D:\workspace\php-sdk\php-sdk-binary-tools-2.2.0\phpdev\vs16\x64\php-8.1.5 +phpsdk_deps -u +``` + +Place extension projects in the `ext` directory of `php-src`, for example: `D:\workspace\php-sdk\php-sdk-binary-tools-2.2.0\phpdev\vs16\x64\php-8.1.5\ext\phpy`. You can also use the `mklink` command to create a symbolic link. + + + +## Compilation Configuration + +```shell +$ buildconf --force +Rebuilding configure.js +Now run 'configure --help' +configure --with-openssl --with-mysqlnd --with-mysqli --enable-mbstring --enable-pdo --with-pdo-mysql --with-curl --enable-cli --enable-opcache --disable-zts --enable-phpy=shared +``` + +`--enable-phpy=shared` indicates enabling the `phpy` extension and compiling it into a `.dll` dynamic link library. + +After successful execution, the output: + +```shell +... + +Generating main/config.w32.h +Generating phpize +Done. + + +Enabled extensions: +----------------------- +| Extension | Mode | +----------------------- +| date | static | +| hash | static | +| json | static | +| pcre | static | +| phpy | shared | +| reflection | static | +| spl | static | +| standard | static | +----------------------- + + +Enabled SAPI: +------------- +| Sapi Name | +------------- +| cli | +------------- + + +--------------------------------------- +| | | +--------------------------------------- +| Build type | Release | +| Thread Safety | No | +| Compiler | Visual C++ 2019 | +| Architecture | x64 | +| Optimization | PGO disabled | +| Native intrinsics | SSE2 | +| Static analyzer | disabled | +--------------------------------------- + + +Type 'nmake' to build PHP + +D:\workspace\php-sdk\php-sdk-binary-tools-2.2.0\phpdev\vs16\x64\php-8.1.5 +$ +``` + + + +## Compile Extensions +```shell +nmake clean +nmake +``` + +## Binary Packaging + +```shell +nmake snap +``` + +After successful execution, `php-8.1.5-nts-Win32-vs16-x64.zip` will be generated in `D:\workspace\php-sdk\php-sdk-binary-tools-2.2.0\phpdev\vs16\x64\php-8.1.5\x64\Release`. diff --git a/docs/en/php/inherit.md b/docs/en/php/inherit.md new file mode 100644 index 0000000..0940022 --- /dev/null +++ b/docs/en/php/inherit.md @@ -0,0 +1,132 @@ +# Inheriting `Python` Classes + +You can use the following method to make a `Python` class the parent class of a `PHP` class. + +## Install Composer Package + +This function requires an additional installation of the `phpy` composer extension package. + +```shell +composer require swoole/phpy +``` + +## Write PHP Class +```php +use phpy\PyClass; + +#[Inherit('Animal', 'animal')] +class Dog extends PyClass +{ + protected string $weight; + + function __construct(string $name, int $age) + { + parent::__construct(); + // This attribute is not defined in the PHP layer and will be set as a Python attribute + $this->color = 'black'; + // This attribute is defined in the PHP layer and will not be set as a Python attribute + $this->weight = '10kg'; + // Read and write Python attribute + $this->self()->color = 'black'; + // Call Python method + $this->get_age(); + $this->self()->get_age(); + // Call parent class constructor + $this->super()->__init__($name, $age); + } + + public function speak(string $name): void + { + echo "Dog $name, color: {$this->self()->color}, speak: wang wang wang\n"; + $this->super()->speak('dog'); + } + + protected function test() + { + debug_print_backtrace(); + } + + // This method will not be mapped to the Python layer and cannot be used in Python + private function get_weight(): string + { + return $this->weight; + } +} +``` + +- `PHP` classes must inherit from the `PyClass` base class. +- Use the `#[Inherit('Animal', 'animal')]` attribute to declare the inheritance relationship. + - The first parameter is the `Python` class name and the second parameter is the `Python` package name. + + - Supports multiple inheritance; you can add multiple `#[Inherit]` attributes. +- The constructor of the subclass must execute the parent class's constructor `parent::__construct()`, or it will throw an error. + + +## Call Base Class Constructor +```php +$this->super()->__init__($name, $age); +``` + +It must be called after `parent::__construct()`, otherwise an error will occur. + + +## Read and Write Properties +```php +$this->self()->color = 'black'; +$this->color = 'red'; +``` + +- If there are properties with the same name in both `PHP` and `Python` classes, the `Python` properties can be accessed using the `$this->self()` method. +- Properties not defined in the `PHP` class can be accessed directly with `$this->{$attr}`, equivalent to `$this->self()->{$attr}`. + + +## Call Methods +```php +$this->self()->get_age(); +$this->get_age(); +``` + +- If there are methods with the same name in both `PHP` and the `Python` parent class, the `Python` methods can be called using `$this->self()->{$method}()`. +- Methods not defined in the `PHP` class can be called directly with `$this->{$method}()`, equivalent to `$this->self()->{$method}()`. + + +## Call Parent Class Methods + +When the subclass and parent class have methods with the same name, you can call the parent class's method using `$this->super()->{$method}()`. + +```php +$this->super()->speak('dog'); +``` + +## Multiple Inheritance + +```php +#[Inherit('Animal', 'animal')] +#[Inherit('Base', 'dog')] +class Dog extends PyClass {} +``` + +This is equivalent to the following `Python` code: +```python +class Dog(Animal, Base): + pass +``` + +## Passing Objects to the `Python` Layer +```php +$framework = PyCore::import('framework'); +$framework->run($this->self()); +``` + +In certain scenarios, it is necessary to pass `PHP` objects to the `Python` layer. You can use the `$this->self()` method to get the `Python` object and pass it to the `Python` layer. When calling the object’s methods internally in `Python`, it will invoke the methods of the `PHP` class. + +> Only `public/protected` methods can be called by `Python`. + + +## Set Proxy File Path +```php +phpy\PyClass::setProxyDir(__DIR__, true); +``` + +- The first parameter is the path for the proxy file, and the `__phpy_proxy__` directory will be created in this directory, generating the proxy file, which defaults to the `getcwd()` path. +- The second parameter determines whether to check if the proxy file is outdated; if the modification time of the proxy file is earlier than that of the `PHP` class file, the proxy file will be regenerated, defaulting to `false`. diff --git a/docs/en/php/memory.md b/docs/en/php/memory.md new file mode 100644 index 0000000..9b4b133 --- /dev/null +++ b/docs/en/php/memory.md @@ -0,0 +1,44 @@ +# Memory Copy + +When `PHP` calls `Python` functions/methods, parameter and return value may involve memory copying, which needs to be considered when writing performance-sensitive programs. + +## Parameters + +- Integer, boolean, floating point, and null (`null`) are always passed by value. + +- Objects, resources, and references are always passed by reference, meaning no memory is copied. +- Strings and arrays will undergo recursive deep copying to convert to native types. + +```php +$user = PyCore::import("user"); +$arg1 = 1234; +$arg2 = 1234.5678; +$arg3 = true; + +$arg4 = new PyDict(); +$arg5 = new stdClass(); +$arg6 = fopen("php://input", "r"); + +$arg7 = ['hello' => 'world']; +$arg8 = "hello world"; +$arg9 = [1, 2, 3, 4, 5]; + +$user->test($arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7, $arg8, $arg9); +``` + +- `$arg1`, `$arg2`, and `$arg3` will be converted to integers, floating point, and boolean in `Python`, copying the values directly. + +- `$arg4`, `$arg5`, and `$arg6` will directly pass references to `Python`, resulting in no memory copies. +- `$arg6`, `$arg7`, and `$arg8` will be traversed and deeply copied in memory, converting to `Python`'s `list`, `dict`, and `str`. + +## Return Values + +- Integers, booleans, floating point numbers, and null (`None` and `null`) will be converted to `PHP`'s native types. + +- All other returned content from `Python` will be of type `PyObject`. +- You can also use `phpy.String` or `phpy.Array` in `Python` code to achieve transparent forwarding of `PHP` native types. + +## Avoiding Memory Copy +Using the `PyObject::object($value)` method can convert string and array types to `PyObject`, where the type obtained in `Python` will be `phpy.String` and `phpy.Array`. + +This method requires the `Python` code to adapt to the `phpy` type system. diff --git a/docs/en/php/socket.md b/docs/en/php/socket.md new file mode 100644 index 0000000..20b3b28 --- /dev/null +++ b/docs/en/php/socket.md @@ -0,0 +1,47 @@ + +Socket API +===== +In `phpy` programming development, it is often necessary to use the `Socket API` to write network communication programs. The following methods can be used to convert the `Python Socket object` and the `PHP Stream resource` to each other. + +Python to PHP +----- +```php +$socket = PyCore::import('socket'); +const HOST = "127.0.0.1"; +const PORT = 5432; + +$client = $socket->socket($socket->AF_INET, $socket->SOCK_STREAM); +$client->connect(PyCore::tuple([HOST, PORT])); + +$fp = fopen('php://fd/' . $client->fileno(), 'rw'); +fwrite($fp, $msg); +$data = fread($fp, 1024); +fclose($fp); +``` + +- Use the `Python Socket.fileno()` method to get the file descriptor. +- Use `PHP fopen('php://fd/{$fileno}')` to convert the file handle corresponding to the `Socket` into a `PHP Stream`. +- Use `fwrite/fread/fclose` to read, write, and close the handle. + +`fopen()` will copy the file descriptor, so calling `fclose()` will not affect the `Python Socket object`, and you can continue to use the `Python Socket API` to perform data transmission operations on this network connection. + +PHP to Python +---- +```php +$socket = PyCore::import('socket'); + +$HOST = "127.0.0.1"; +$PORT = 5432; + +$fp = stream_socket_client("tcp://$HOST:$PORT", $errno, $errstr, 30); +if (!$fp) { + exit("Error: $errstr ($errno)\n"); +} +$client = $socket->fromfd(PyCore::fileno($fp), $socket->AF_INET, $socket->SOCK_STREAM); +fclose($fp); +``` + +- Use the `PyCore::fileno($fp)` method to obtain the `PHP Stream` file descriptor. +- Use `Python Socket.fromfd()` to convert the file handle corresponding to the `Socket` into a `Python Socket object`. + +`Socket.fromfd()` will copy the file descriptor, so closing the `Python Socket` will not affect the `PHP Stream`, and you can still use `PHP`'s `fwrite/fread/fclose` to perform data transmission operations on this network connection. diff --git a/docs/en/python/array.md b/docs/en/python/array.md new file mode 100644 index 0000000..b5723bc --- /dev/null +++ b/docs/en/python/array.md @@ -0,0 +1,52 @@ +# Array Operations + +`phpy.Array` is a mixed type that can either be a `List` or a `Map`. + + +## Creation +```python +# List +l = phpy.Array([1, 3, 5, 2023, 7, 9]) +# Map +m = phpy.Array({"hello": "world", "php": "swoole"}) +``` + + +## Reading +```python +print(l[3]) +print(m["php"]) +``` + + +## Length +```python +print(len(l)) +print(len(m)) +``` + + +## Writing +```python +# Set Key Value +m["swoole"] = 'coroutine' +# Append element to the end +l.append(9999) +``` + + + + +## Other Methods + +- `get(key)` Read + +- `set(key, value)` Write + +- `unset(key)` Delete + +- `append(value)` Append element to the end of the list + +- `count()` Read elements +- `collect()` Convert to native `Python` `dict` or `list` +- `is_list()` Check if the array is a `List` diff --git a/docs/en/python/build.md b/docs/en/python/build.md new file mode 100644 index 0000000..b4cacea --- /dev/null +++ b/docs/en/python/build.md @@ -0,0 +1,61 @@ +Building a Python Module +==== +When calling PHP functions from Python, you need to build the `phpy` module for Python. + +## Compilation Dependencies + +- `cmake 3.16` or higher + +- `php 8.1 (embed)` or higher, you need to add the `--enable-embed` parameter when compiling PHP +- `Python 3.8` or higher, you need to install `python3-dev` + +## Compilation Configuration + +### `PHP_CONFIG` + +Specify the path to the `php-config` command, default is relative path, for example: + +```shell +cmake . -D PHP_CONFIG=/usr/local/php/bin/php-config +``` + +### `PYTHON_CONFIG` + +Specify the path to the `python-config` command, default is relative path, for example: + +```shell +cmake . -D PYTHON_CONFIG=/usr/local/bin/python3-config +``` + +## Build +```shell +make -j 4 +``` + +After successful compilation, `phpy.so` will be generated in the `lib` directory, and this file can be copied to any directory in Python's `sys.path`. + +## conda Tool + +You can use the `conda` tool to manage Python environments. + +### Create Python Environment + +```shell +conda create -n py38 python=3.8 +# Activate +conda activate py38 +``` + +### `pip` Acceleration +```shell +# Aliyun +pip config set global.index-url https://mirrors.aliyun.com/pypi/simple +# Tsinghua Source +pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple +``` + +## Unit Testing +```shell +pip install pytest +pytest -v tests/ +``` diff --git a/docs/en/python/memory.md b/docs/en/python/memory.md new file mode 100644 index 0000000..77a72cf --- /dev/null +++ b/docs/en/python/memory.md @@ -0,0 +1,42 @@ +# Memory Copy + +When `Python` calls `PHP` functions/methods, parameters and return values may involve memory copying. It is important to pay attention to the overhead of memory copying when writing performance-sensitive programs. + +## Parameters + +- Integer, Boolean, Float, and None (`None`) are always passed by value. + +- Objects, Resources, and References are always passed by reference, and no memory is copied. +- Strings and Arrays will be recursively deep-copied into native types. + +```python + +arg1 = 1234 +arg2 = 1234.5678 +arg3 = True + +arg4 = phpy.Object('stdClass') +arg5 = phpy.Reference() +arg6 = phpy.call('fopen', "php://input", "r") + +arg7 = {'hello' : 'world'} +arg8 = "hello world" +arg9 = [1, 2, 3, 4, 5] + +phpy.call('test', arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) +``` + +- `arg1`, `arg2`, and `arg3` will be converted to integers, floats, and booleans in `PHP`, copying the values directly. + +- `arg4`, `arg5`, and `arg6` will directly pass references to `PHP`, producing no memory copy. +- `arg6`, `arg7`, and `arg8` will be traversed, deeply copied in memory, and converted into `PHP`’s `array` and `string`. + +## Return Values + +- Integers, Booleans, Floats, and None (`None` and `null`) will be converted to Python's native types. + +- All other content returned from `Python` will be of `PyObject` type. +- You can also use `PyObject` type in `PHP` code, allowing for transparent forwarding to return Python's native types. + +## Avoid Memory Copy +By using `phpy.String` and `phpy.Array` objects when calling PHP functions, memory copying will not occur. diff --git a/docs/en/python/string.md b/docs/en/python/string.md new file mode 100644 index 0000000..2080dfe --- /dev/null +++ b/docs/en/python/string.md @@ -0,0 +1,61 @@ +# String Operations +`phpy.String` string type is equivalent to a byte array in `Python`. The content does not contain encoding information and may contain binary content. It is not the same as `Python`'s `str` type. The `phpy` underlying system provides an API similar to the `str` style to operate on strings. + + +## Creation +```python +s1 = phpy.String("hello world") +s2 = phpy.call("random_bytes", 128) +``` + + +## Length +```python +print(len(s1)) +print(len(s2)) +``` + + +## Append +```python +# Append str +s1 += "hello" +# Append bytes +s1 += b"world" +# Append another phpy.String +s1 += phpy.String(", php is the best programming language") +``` + + +## Containment +```python +# Returns True +print(s1.__contains__("php")) +# Returns False +print(s1.__contains__("java")) +``` + + +## Comparison +```python +s3 = phpy.String("hello") +if s3 == "hello": + print("==") +``` + + +## Get Byte +```python +print(s1[2]) +``` + +Please note that the format returned by `phpy.String` is consistent with `bytes`, which is a `uchar`. However, it differs from `str`; `str` handles `UTF-8` encoding and returns a `UTF-8` wide character, for example, `str('中国')[0]` returns `中`. + + +## Convert to `Python`'s `bytes` Type + +```python +print(bytes(s1)) +``` + +> Please note that this will result in a memory copy.