Giter Site home page Giter Site logo

swoole / phpy Goto Github PK

View Code? Open in Web Editor NEW
496.0 13.0 40.0 1.53 MB

Connecting the Python and PHP ecosystems together

License: Apache License 2.0

CMake 0.13% Shell 0.01% M4 0.18% C++ 9.99% PHP 11.68% Python 75.96% C 1.82% JavaScript 0.07% Dockerfile 0.04% Smarty 0.12%

phpy's Introduction

简体中文

phpy

A library for inter-calling Python and PHP. You can use Python functions and libraries in PHP, or use PHP packages in Python.

See documents: docs/en/README.md

Supports Linux/Windows/macOS Not support Python multithreading or async-io features

py2php

py2php is online utility that will auto-translate python code into PHP code.

Calling Python from PHP

Compile and install phpy.so as an extension, and append extension=phpy.so to php.ini.

PHP Example:

$os = PyCore::import("os");
echo $os->uname();

Transformers

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

$os = PyCore::import('os');
$os->environ['https_proxy'] = getenv('https_proxy');

$tokenizer = $AutoTokenizer->from_pretrained("lxyuan/distilbert-base-multilingual-cased-sentiments-student");
$model = $AutoModelForSequenceClassification->from_pretrained("lxyuan/distilbert-base-multilingual-cased-sentiments-student");

Calling PHP from Python

Simply import it as a C++ Mudule.

Python Example:

import phpy
content = phpy.call('file_get_contents', 'test.txt')

o = phpy.Object('redis')
assert o.call('connect', '127.0.0.1', 6379)
rdata = phpy.call('uniqid')
assert o.call('set', 'key', rdata)
assert o.call('get', 'key') == rdata

Implementation

It creates ZendVM and CPython VM in the process at the same time, and directly uses C functions to call each other in the process stack space.

The overhead is only the conversion of zval <-> PyObject structures, so the performance is very high.

In the benchmark test, we created a PyDict and executed PHP code and Python code to read and write 10 million times respectively.

The performance of phpy writing PyDict with PHP code is 14% higher than the native Python, and the read performance is 25% higher.

More details: docs/en/benchmark.md

phpy's People

Contributors

baicaiit avatar he426100 avatar heelie avatar matyhtf avatar netyum avatar sqsora avatar tinywan avatar yurunsoft avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

phpy's Issues

导入第三方包的时候出现错误

image

image

本地机器测试正常没问题,部署到服务器上就出现这个错误。

并且导入内置包没事,比如:PyCore::import('os');

能否帮忙分析下是什么原因导致的吗?

Dockerfile.alpine执行构建镜像失败

{"stream":"Step 1/14 : FROM php:8.2.13-cli-alpine"} {"stream":"\n"} {"status":"Pulling from library/php","id":"8.2.13-cli-alpine"} {"status":"Pulling fs layer","progressDetail":{},"id":"661ff4d9561e"} {"status":"Pulling fs layer","progressDetail":{},"id":"fbc99979baa6"} {"status":"Pulling fs layer","progressDetail":{},"id":"47536423e079"} {"status":"Pulling fs layer","progressDetail":{},"id":"1be6d4fc569f"} {"status":"Pulling fs layer","progressDetail":{},"id":"2014613d03d8"} {"status":"Pulling fs layer","progressDetail":{},"id":"8af6c37b7e86"} {"status":"Pulling fs layer","progressDetail":{},"id":"7926b59062ab"} {"status":"Pulling fs layer","progressDetail":{},"id":"c0cfee82d291"} {"status":"Pulling fs layer","progressDetail":{},"id":"1cf6a35fce59"} {"status":"Waiting","progressDetail":{},"id":"1be6d4fc569f"} {"status":"Waiting","progressDetail":{},"id":"8af6c37b7e86"} {"status":"Waiting","progressDetail":{},"id":"2014613d03d8"} {"status":"Waiting","progressDetail":{},"id":"7926b59062ab"} {"status":"Waiting","progressDetail":{},"id":"1cf6a35fce59"} {"status":"Waiting","progressDetail":{},"id":"c0cfee82d291"} {"status":"Downloading","progressDetail":{"current":36422,"total":3408480},"progress":"[> ] 36.42kB/3.408MB","id":"661ff4d9561e"} {"status":"Downloading","progressDetail":{"current":1262,"total":1262},"progress":"[==================================================>] 1.262kB/1.262kB","id":"47536423e079"} {"status":"Download complete","progressDetail":{},"id":"47536423e079"} {"status":"Downloading","progressDetail":{"current":589382,"total":3408480},"progress":"[========> ] 589.4kB/3.408MB","id":"661ff4d9561e"} {"status":"Downloading","progressDetail":{"current":28228,"total":2755737},"progress":"[> ] 28.23kB/2.756MB","id":"fbc99979baa6"} {"status":"Downloading","progressDetail":{"current":1474118,"total":3408480},"progress":"[=====================> ] 1.474MB/3.408MB","id":"661ff4d9561e"} {"status":"Downloading","progressDetail":{"current":3022406,"total":3408480},"progress":"[============================================> ] 3.022MB/3.408MB","id":"661ff4d9561e"} {"status":"Verifying Checksum","progressDetail":{},"id":"661ff4d9561e"} {"status":"Download complete","progressDetail":{},"id":"661ff4d9561e"} {"status":"Downloading","progressDetail":{"current":56900,"total":2755737},"progress":"[=> ] 56.9kB/2.756MB","id":"fbc99979baa6"} {"status":"Extracting","progressDetail":{"current":65536,"total":3408480},"progress":"[> ] 65.54kB/3.408MB","id":"661ff4d9561e"} {"status":"Downloading","progressDetail":{"current":228932,"total":2755737},"progress":"[====> ] 228.9kB/2.756MB","id":"fbc99979baa6"} {"status":"Extracting","progressDetail":{"current":1638400,"total":3408480},"progress":"[========================> ] 1.638MB/3.408MB","id":"661ff4d9561e"} {"status":"Downloading","progressDetail":{"current":400964,"total":2755737},"progress":"[=======> ] 401kB/2.756MB","id":"fbc99979baa6"} {"status":"Extracting","progressDetail":{"current":3342336,"total":3408480},"progress":"[=================================================> ] 3.342MB/3.408MB","id":"661ff4d9561e"} {"status":"Extracting","progressDetail":{"current":3408480,"total":3408480},"progress":"[==================================================>] 3.408MB/3.408MB","id":"661ff4d9561e"} {"status":"Downloading","progressDetail":{"current":601668,"total":2755737},"progress":"[==========> ] 601.7kB/2.756MB","id":"fbc99979baa6"} {"status":"Pull complete","progressDetail":{},"id":"661ff4d9561e"} {"status":"Downloading","progressDetail":{"current":1031748,"total":2755737},"progress":"[==================> ] 1.032MB/2.756MB","id":"fbc99979baa6"} {"status":"Downloading","progressDetail":{"current":269,"total":269},"progress":"[==================================================>] 269B/269B","id":"1be6d4fc569f"} {"status":"Verifying Checksum","progressDetail":{},"id":"1be6d4fc569f"} {"status":"Download complete","progressDetail":{},"id":"1be6d4fc569f"} {"status":"Downloading","progressDetail":{"current":1891908,"total":2755737},"progress":"[==================================> ] 1.892MB/2.756MB","id":"fbc99979baa6"} {"status":"Verifying Checksum","progressDetail":{},"id":"fbc99979baa6"} {"status":"Download complete","progressDetail":{},"id":"fbc99979baa6"} {"status":"Extracting","progressDetail":{"current":32768,"total":2755737},"progress":"[> ] 32.77kB/2.756MB","id":"fbc99979baa6"} {"status":"Extracting","progressDetail":{"current":393216,"total":2755737},"progress":"[=======> ] 393.2kB/2.756MB","id":"fbc99979baa6"} {"status":"Downloading","progressDetail":{"current":122442,"total":12090156},"progress":"[> ] 122.4kB/12.09MB","id":"2014613d03d8"} {"status":"Extracting","progressDetail":{"current":2293760,"total":2755737},"progress":"[=========================================> ] 2.294MB/2.756MB","id":"fbc99979baa6"} {"status":"Extracting","progressDetail":{"current":2755737,"total":2755737},"progress":"[==================================================>] 2.756MB/2.756MB","id":"fbc99979baa6"} {"status":"Downloading","progressDetail":{"current":982602,"total":12090156},"progress":"[====> ] 982.6kB/12.09MB","id":"2014613d03d8"} {"status":"Downloading","progressDetail":{"current":495,"total":495},"progress":"[==================================================>] 495B/495B","id":"8af6c37b7e86"} {"status":"Download complete","progressDetail":{},"id":"8af6c37b7e86"} {"status":"Pull complete","progressDetail":{},"id":"fbc99979baa6"} {"status":"Extracting","progressDetail":{"current":1262,"total":1262},"progress":"[==================================================>] 1.262kB/1.262kB","id":"47536423e079"} {"status":"Downloading","progressDetail":{"current":1597002,"total":12090156},"progress":"[======> ] 1.597MB/12.09MB","id":"2014613d03d8"} {"status":"Extracting","progressDetail":{"current":1262,"total":1262},"progress":"[==================================================>] 1.262kB/1.262kB","id":"47536423e079"} {"status":"Pull complete","progressDetail":{},"id":"47536423e079"} {"status":"Downloading","progressDetail":{"current":3194442,"total":12090156},"progress":"[=============> ] 3.194MB/12.09MB","id":"2014613d03d8"} {"status":"Extracting","progressDetail":{"current":269,"total":269},"progress":"[==================================================>] 269B/269B","id":"1be6d4fc569f"} {"status":"Extracting","progressDetail":{"current":269,"total":269},"progress":"[==================================================>] 269B/269B","id":"1be6d4fc569f"} {"status":"Downloading","progressDetail":{"current":5160522,"total":12090156},"progress":"[=====================> ] 5.161MB/12.09MB","id":"2014613d03d8"} {"status":"Pull complete","progressDetail":{},"id":"1be6d4fc569f"} {"status":"Downloading","progressDetail":{"current":6389322,"total":12090156},"progress":"[==========================> ] 6.389MB/12.09MB","id":"2014613d03d8"} {"status":"Downloading","progressDetail":{"current":171593,"total":16904612},"progress":"[> ] 171.6kB/16.9MB","id":"7926b59062ab"} {"status":"Downloading","progressDetail":{"current":7495242,"total":12090156},"progress":"[==============================> ] 7.495MB/12.09MB","id":"2014613d03d8"} {"status":"Downloading","progressDetail":{"current":8478282,"total":12090156},"progress":"[===================================> ] 8.478MB/12.09MB","id":"2014613d03d8"} {"status":"Downloading","progressDetail":{"current":515657,"total":16904612},"progress":"[=> ] 515.7kB/16.9MB","id":"7926b59062ab"} {"status":"Downloading","progressDetail":{"current":9215562,"total":12090156},"progress":"[======================================> ] 9.216MB/12.09MB","id":"2014613d03d8"} {"status":"Downloading","progressDetail":{"current":859721,"total":16904612},"progress":"[==> ] 859.7kB/16.9MB","id":"7926b59062ab"} {"status":"Downloading","progressDetail":{"current":9829962,"total":12090156},"progress":"[========================================> ] 9.83MB/12.09MB","id":"2014613d03d8"} {"status":"Downloading","progressDetail":{"current":10935882,"total":12090156},"progress":"[=============================================> ] 10.94MB/12.09MB","id":"2014613d03d8"} {"status":"Downloading","progressDetail":{"current":1375817,"total":16904612},"progress":"[====> ] 1.376MB/16.9MB","id":"7926b59062ab"} {"status":"Downloading","progressDetail":{"current":11918922,"total":12090156},"progress":"[=================================================> ] 11.92MB/12.09MB","id":"2014613d03d8"} {"status":"Verifying Checksum","progressDetail":{},"id":"2014613d03d8"} {"status":"Download complete","progressDetail":{},"id":"2014613d03d8"} {"status":"Extracting","progressDetail":{"current":131072,"total":12090156},"progress":"[> ] 131.1kB/12.09MB","id":"2014613d03d8"} {"status":"Downloading","progressDetail":{"current":2408009,"total":16904612},"progress":"[=======> ] 2.408MB/16.9MB","id":"7926b59062ab"} {"status":"Extracting","progressDetail":{"current":8912896,"total":12090156},"progress":"[====================================> ] 8.913MB/12.09MB","id":"2014613d03d8"} {"status":"Downloading","progressDetail":{"current":3268169,"total":16904612},"progress":"[=========> ] 3.268MB/16.9MB","id":"7926b59062ab"} {"status":"Extracting","progressDetail":{"current":12090156,"total":12090156},"progress":"[==================================================>] 12.09MB/12.09MB","id":"2014613d03d8"} {"status":"Downloading","progressDetail":{"current":4472393,"total":16904612},"progress":"[=============> ] 4.472MB/16.9MB","id":"7926b59062ab"} {"status":"Pull complete","progressDetail":{},"id":"2014613d03d8"} {"status":"Extracting","progressDetail":{"current":495,"total":495},"progress":"[==================================================>] 495B/495B","id":"8af6c37b7e86"} {"status":"Extracting","progressDetail":{"current":495,"total":495},"progress":"[==================================================>] 495B/495B","id":"8af6c37b7e86"} {"status":"Downloading","progressDetail":{"current":5676617,"total":16904612},"progress":"[================> ] 5.677MB/16.9MB","id":"7926b59062ab"} {"status":"Downloading","progressDetail":{"current":2446,"total":2446},"progress":"[==================================================>] 2.446kB/2.446kB","id":"c0cfee82d291"} {"status":"Verifying Checksum","progressDetail":{},"id":"c0cfee82d291"} {"status":"Download complete","progressDetail":{},"id":"c0cfee82d291"} {"status":"Pull complete","progressDetail":{},"id":"8af6c37b7e86"} {"status":"Downloading","progressDetail":{"current":6880841,"total":16904612},"progress":"[====================> ] 6.881MB/16.9MB","id":"7926b59062ab"} {"status":"Downloading","progressDetail":{"current":3662,"total":19288},"progress":"[=========> ] 3.662kB/19.29kB","id":"1cf6a35fce59"} {"status":"Downloading","progressDetail":{"current":19288,"total":19288},"progress":"[==================================================>] 19.29kB/19.29kB","id":"1cf6a35fce59"} {"status":"Verifying Checksum","progressDetail":{},"id":"1cf6a35fce59"} {"status":"Download complete","progressDetail":{},"id":"1cf6a35fce59"} {"status":"Downloading","progressDetail":{"current":8085065,"total":16904612},"progress":"[=======================> ] 8.085MB/16.9MB","id":"7926b59062ab"} {"status":"Downloading","progressDetail":{"current":9289289,"total":16904612},"progress":"[===========================> ] 9.289MB/16.9MB","id":"7926b59062ab"} {"status":"Downloading","progressDetail":{"current":10493513,"total":16904612},"progress":"[===============================> ] 10.49MB/16.9MB","id":"7926b59062ab"} {"status":"Downloading","progressDetail":{"current":11697737,"total":16904612},"progress":"[==================================> ] 11.7MB/16.9MB","id":"7926b59062ab"} {"status":"Downloading","progressDetail":{"current":12901961,"total":16904612},"progress":"[======================================> ] 12.9MB/16.9MB","id":"7926b59062ab"} {"status":"Downloading","progressDetail":{"current":14106185,"total":16904612},"progress":"[=========================================> ] 14.11MB/16.9MB","id":"7926b59062ab"} {"status":"Downloading","progressDetail":{"current":15310409,"total":16904612},"progress":"[=============================================> ] 15.31MB/16.9MB","id":"7926b59062ab"} {"status":"Downloading","progressDetail":{"current":16514633,"total":16904612},"progress":"[================================================> ] 16.51MB/16.9MB","id":"7926b59062ab"} {"status":"Verifying Checksum","progressDetail":{},"id":"7926b59062ab"} {"status":"Download complete","progressDetail":{},"id":"7926b59062ab"} {"status":"Extracting","progressDetail":{"current":196608,"total":16904612},"progress":"[> ] 196.6kB/16.9MB","id":"7926b59062ab"} {"status":"Extracting","progressDetail":{"current":2555904,"total":16904612},"progress":"[=======> ] 2.556MB/16.9MB","id":"7926b59062ab"} {"status":"Extracting","progressDetail":{"current":4128768,"total":16904612},"progress":"[============> ] 4.129MB/16.9MB","id":"7926b59062ab"} {"status":"Extracting","progressDetail":{"current":5701632,"total":16904612},"progress":"[================> ] 5.702MB/16.9MB","id":"7926b59062ab"} {"status":"Extracting","progressDetail":{"current":6684672,"total":16904612},"progress":"[===================> ] 6.685MB/16.9MB","id":"7926b59062ab"} {"status":"Extracting","progressDetail":{"current":8060928,"total":16904612},"progress":"[=======================> ] 8.061MB/16.9MB","id":"7926b59062ab"} {"status":"Extracting","progressDetail":{"current":9633792,"total":16904612},"progress":"[============================> ] 9.634MB/16.9MB","id":"7926b59062ab"} {"status":"Extracting","progressDetail":{"current":10027008,"total":16904612},"progress":"[=============================> ] 10.03MB/16.9MB","id":"7926b59062ab"} {"status":"Extracting","progressDetail":{"current":11599872,"total":16904612},"progress":"[==================================> ] 11.6MB/16.9MB","id":"7926b59062ab"} {"status":"Extracting","progressDetail":{"current":13565952,"total":16904612},"progress":"[========================================> ] 13.57MB/16.9MB","id":"7926b59062ab"} {"status":"Extracting","progressDetail":{"current":14155776,"total":16904612},"progress":"[=========================================> ] 14.16MB/16.9MB","id":"7926b59062ab"} {"status":"Extracting","progressDetail":{"current":15335424,"total":16904612},"progress":"[=============================================> ] 15.34MB/16.9MB","id":"7926b59062ab"} {"status":"Extracting","progressDetail":{"current":15925248,"total":16904612},"progress":"[===============================================> ] 15.93MB/16.9MB","id":"7926b59062ab"} {"status":"Extracting","progressDetail":{"current":16904612,"total":16904612},"progress":"[==================================================>] 16.9MB/16.9MB","id":"7926b59062ab"} {"status":"Extracting","progressDetail":{"current":16904612,"total":16904612},"progress":"[==================================================>] 16.9MB/16.9MB","id":"7926b59062ab"} {"status":"Pull complete","progressDetail":{},"id":"7926b59062ab"} {"status":"Extracting","progressDetail":{"current":2446,"total":2446},"progress":"[==================================================>] 2.446kB/2.446kB","id":"c0cfee82d291"} {"status":"Extracting","progressDetail":{"current":2446,"total":2446},"progress":"[==================================================>] 2.446kB/2.446kB","id":"c0cfee82d291"} {"status":"Pull complete","progressDetail":{},"id":"c0cfee82d291"} {"status":"Extracting","progressDetail":{"current":19288,"total":19288},"progress":"[==================================================>] 19.29kB/19.29kB","id":"1cf6a35fce59"} {"status":"Extracting","progressDetail":{"current":19288,"total":19288},"progress":"[==================================================>] 19.29kB/19.29kB","id":"1cf6a35fce59"} {"status":"Pull complete","progressDetail":{},"id":"1cf6a35fce59"} {"status":"Digest: sha256:3b0171123d74f66754dbe4c425430a985710881f9e34c31263913555d8963354"} {"status":"Status: Downloaded newer image for php:8.2.13-cli-alpine"} {"stream":" ---> bcd00d062cc1\n"} {"stream":"Step 2/14 : RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories"} {"stream":"\n"} {"stream":" ---> Running in 970eaf43f66f\n"} {"stream":"Removing intermediate container 970eaf43f66f\n"} {"stream":" ---> 1c96956f0b31\n"} {"stream":"Step 3/14 : RUN apk add python3"} {"stream":"\n"} {"stream":" ---> Running in b599e8699b7c\n"} {"stream":"fetch https://mirrors.ustc.edu.cn/alpine/v3.19/main/x86_64/APKINDEX.tar.gz\n"} {"stream":"fetch https://mirrors.ustc.edu.cn/alpine/v3.19/community/x86_64/APKINDEX.tar.gz\n"} {"stream":"(1/11) Installing libexpat (2.6.2-r0)\n"} {"stream":"(2/11) Installing libbz2 (1.0.8-r6)\n"} {"stream":"(3/11) Installing libffi (3.4.4-r3)\n"} {"stream":"(4/11) Installing gdbm (1.23-r1)\n"} {"stream":"(5/11) Installing libstdc++ (13.2.1_git20231014-r0)\n"} {"stream":"(6/11) Installing mpdecimal (2.5.1-r2)\n"} {"stream":"(7/11) Installing libpanelw (6.4_p20231125-r0)\n"} {"stream":"(8/11) Installing python3 (3.11.9-r0)\n"} {"stream":"(9/11) Installing python3-pycache-pyc0 (3.11.9-r0)\n"} {"stream":"(10/11) Installing pyc (3.11.9-r0)\n"} {"stream":"(11/11) Installing python3-pyc (3.11.9-r0)\n"} {"stream":"Executing busybox-1.36.1-r15.trigger\n"} {"stream":"OK: 60 MiB in 49 packages\n"} {"stream":"Removing intermediate container b599e8699b7c\n"} {"stream":" ---> 0faa63180c93\n"} {"stream":"Step 4/14 : RUN apk add autoconf gcc g++ make python3-dev"} {"stream":"\n"} {"stream":" ---> Running in 262ccb92157f\n"} {"stream":"(1/20) Installing m4 (1.4.19-r3)\n"} {"stream":"(2/20) Installing perl (5.38.2-r0)\n"} {"stream":"(3/20) Installing autoconf (2.71-r2)\n"} {"stream":"(4/20) Installing libstdc++-dev (13.2.1_git20231014-r0)\n"} {"stream":"(5/20) Installing jansson (2.14-r4)\n"} {"stream":"(6/20) Installing zstd-libs (1.5.5-r8)\n"} {"stream":"(7/20) Installing binutils (2.41-r0)\n"} {"stream":"(8/20) Installing libgomp (13.2.1_git20231014-r0)\n"} {"stream":"(9/20) Installing libatomic (13.2.1_git20231014-r0)\n"} {"stream":"(10/20) Installing gmp (6.3.0-r0)\n"} {"stream":"(11/20) Installing isl26 (0.26-r1)\n"} {"stream":"(12/20) Installing mpfr4 (4.2.1-r0)\n"} {"stream":"(13/20) Installing mpc1 (1.3.1-r1)\n"} {"stream":"(14/20) Installing gcc (13.2.1_git20231014-r0)\n"} {"stream":"(15/20) Installing musl-dev (1.2.4_git20230717-r4)\n"} {"stream":"(16/20) Installing libc-dev (0.7.2-r5)\n"} {"stream":"(17/20) Installing g++ (13.2.1_git20231014-r0)\n"} {"stream":"(18/20) Installing make (4.4.1-r2)\n"} {"stream":"(19/20) Installing pkgconf (2.1.0-r0)\n"} {"stream":"(20/20) Installing python3-dev (3.11.9-r0)\n"} {"stream":"Executing busybox-1.36.1-r15.trigger\n"} {"stream":"OK: 375 MiB in 69 packages\n"} {"stream":"Removing intermediate container 262ccb92157f\n"} {"stream":" ---> fc16379800db\n"} {"stream":"Step 5/14 : WORKDIR /work"} {"stream":"\n"} {"stream":" ---> Running in 7cb1d880fda1\n"} {"stream":"Removing intermediate container 7cb1d880fda1\n"} {"stream":" ---> 902c51742dbb\n"} {"stream":"Step 6/14 : COPY . /work/phpy"} {"stream":"\n"} {"stream":" ---> 1993282589b5\n"} {"stream":"Step 7/14 : RUN docker-php-source extract && cd /work/phpy && phpize && ./configure --with-python-config=/usr/bin/python3-config && make clean && make -j $(nproc)"} {"stream":"\n"} {"stream":" ---> Running in 8ee8d933f1e1\n"} {"stream":"Cannot find config.m4. \nMake sure that you run '/usr/local/bin/phpize' in the top level source directory of the module\n\n"} {"errorDetail":{"code":1,"message":"The command '/bin/sh -c docker-php-source extract && cd /work/phpy && phpize && ./configure --with-python-config=/usr/bin/python3-config && make clean && make -j $(nproc)' returned a non-zero code: 1"},"error":"The command '/bin/sh -c docker-php-source extract && cd /work/phpy && phpize && ./configure --with-python-config=/usr/bin/python3-config && make clean && make -j $(nproc)' returned a non-zero code: 1"} image build failed!
image

使用官方的Dockerfile:https://github.com/swoole/phpy/blob/main/Dockerfile.alpine
`FROM php:8.2.13-cli-alpine

RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
RUN apk add python3
RUN apk add autoconf gcc g++ make python3-dev

WORKDIR /work
COPY . /work/phpy

RUN docker-php-source extract &&
cd /work/phpy && phpize &&
./configure --with-python-config=/usr/bin/python3-config &&
make clean && make -j $(nproc)

FROM php:8.2.13-cli-alpine
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
RUN apk add python3 bash

COPY --from=0 /work/phpy/modules/phpy.so /tmp
RUN cp /tmp/phpy.so $(php-config --extension-dir) &&
docker-php-ext-enable phpy &&
docker-php-source delete
RUN php --ri phpy

CMD ["bash"]`

任意调用python内置函数及其他

  • 原理
    python的内置函数是通过 import builtins导入的,用 PyCore::import('builtins')后就可以调用任何函数了

  • 示例

<?php

class MyPy extends PyCore
{
    protected static $py;

    public static function instance()
    {
        if (!self::$py) {
            self::$py = self::import('builtins');
        }
        return self::$py;
    }

   public static function dump($e)
    {
        var_dump(self::scalar($e));
    }

    public static function __callStatic($name, $arguments)
    {
        return self::instance()->$name(...$arguments);
    }
}

> MyPy::dump(MyPy::range(1, 10, 2))
array(5) {
  [0]=>
  int(1)
  [1]=>
  int(3)
  [2]=>
  int(5)
  [3]=>
  int(7)
  [4]=>
  int(9)
}

Python.h: No such file or directory

sudo make install报错:

libtool: compile:  g++ -I. -I/home/mememe/phpy -I/home/mememe/phpy/include -I/home/mememe/phpy/main -I/home/mememe/phpy -I/usr/include/php/20220829 -I/usr/include/php/20220829/main -I/usr/include/php/20220829/TSRM -I/usr/include/php/20220829/Zend -I/usr/include/php/20220829/ext -I/usr/include/php/20220829/ext/date/lib -I/opt/miniconda3/include/python3.11 -DHAVE_CONFIG_H -g -O2 -Wall -Wno-unused-function -Wno-deprecated -Wno-deprecated-declarations -z now -std=c++14 -DZEND_COMPILE_DL_EXT=1 -c /home/mememe/phpy/src/bridge/core.cc -MMD -MF src/bridge/core.dep -MT src/bridge/core.lo  -fPIC -DPIC -o src/bridge/.libs/core.o
In file included from /home/mememe/phpy/src/bridge/core.cc:18:
/home/mememe/phpy/include/phpy.h:21:10: fatal error: Python.h: No such file or directory
   21 | #include <Python.h>
      |          ^~~~~~~~~~
compilation terminated.
make: *** [Makefile:213: src/bridge/core.lo] Error 1

py2php转换错误例子

  1. Parse error: syntax error, unexpected token ")" on line 18
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");
$str_val = "apples";
$num_val = 42;
PyCore::print($num_val . " " . $str_val);
$str_val = "apples";
$num_val = 42;
PyCore::print("str_val=" . $str_val . ", num_val = " . $num_val);
$num_val = 42;
PyCore::print("num_val % 2 = " . $num_val % 2);
$str_val = "apples";
PyCore::print($str_val);
$price_val = 6.12658;
PyCore::print(round($price_val, 2));
$datetime = PyCore::import('datetime')->datetime;
$date_val = $datetime->utcnow();
PyCore::print("date_val=" . );
str_val = 'apples'
num_val = 42

print(f'{num_val} {str_val}') # 42 apples

str_val = 'apples'
num_val = 42

print(f'{str_val=}, {num_val = }') # str_val='apples', num_val = 42

num_val = 42

print(f'{num_val % 2 = }') # num_val % 2 = 0

str_val = 'apples'

print(f'{str_val!r}') # 'apples'

price_val = 6.12658

print(f'{price_val:.2f}') # 6.13

from datetime import datetime;

date_val = datetime.utcnow()

print(f'{date_val=:%Y-%m-%d}') # date_val=2021-07-09
  1. Warning: Undefined variable $timedelta on line 9
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");
$date = PyCore::import('datetime')->date;
$datetime = PyCore::import('datetime')->datetime;
$timedelta = PyCore::import('datetime')->timedelta;

function add_days($n, $d) {
    return $d + $timedelta($n);
}


add_days(5, $date(2020, 10, 25));
add_days(-5, $date(2020, 10, 25));
from datetime import date, datetime, timedelta

def add_days(n, d = datetime.today()):
  return d + timedelta(n)

add_days(5, date(2020, 10, 25)) # date(2020, 10, 30)
add_days(-5, date(2020, 10, 25)) # date(2020, 10, 20)
  1. Parse error: syntax error, unexpected variable "$x", expecting ":" on line 18
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");

function average_by($lst, $fn) {
    return PyCore::sum(PyCore::map($fn, $lst), 0) / PyCore::len($lst);
}


average_by(new PyList([new PyDict([
    "n" => 4,
]), new PyDict([
    "n" => 2,
]), new PyDict([
    "n" => 8,
]), new PyDict([
    "n" => 6,
])]), return $x->__getitem__("n");

});
def average_by(lst, fn = lambda x: x):
  return sum(map(fn, lst), 0.0) / len(lst)

average_by([{ 'n': 4 }, { 'n': 2 }, { 'n': 8 }, { 'n': 6 }], lambda x: x['n'])
# 5.0
  1. Parse error: syntax error, unexpected token "$", expecting variable on line 6
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");

function bifurcate($lst, $filter) {
    return new PyList([(function($) {
        $___ = [];
        $elts = [];
        foreach($ as [$x, $flag]) {
            $elts[0] = [$x, $flag];
        }
        $___[] = $elts;
        return $___;
    })($), (function($) {
        $___ = [];
        $elts = [];
        foreach($ as [$x, $flag]) {
            $elts[0] = [$x, $flag];
        }
        $___[] = $elts;
        return $___;
    })($)]);
}


bifurcate(new PyList(["beep", "boop", "foo", "bar"]), new PyList([true, true, false, true]));
def bifurcate(lst, filter):
  return [
    [x for x, flag in zip(lst, filter) if flag],
    [x for x, flag in zip(lst, filter) if not flag]
  ]

bifurcate(['beep', 'boop', 'foo', 'bar'], [True, True, False, True])
# [ ['beep', 'boop', 'bar'], ['foo'] ]
  1. Fatal error: Uncaught Error: Call to a member function encode() on string in /home/mrpzx/git/phpy/phpy-examples/30s/python-code/byte-size.php:6
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");

function byte_size($s) {
    return PyCore::len($s->encode("utf-8"));
}


byte_size("😀");
byte_size("Hello World");
def byte_size(s):
  return len(s.encode('utf-8'))

byte_size('😀') # 4
byte_size('Hello World') # 11
  1. Fatal error: Uncaught Error: Call to a member function title() on string in /home/mrpzx/git/phpy/phpy-examples/30s/python-code/capitalize-every-word.php:6
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");

function capitalize_every_word($s) {
    return $s->title();
}


capitalize_every_word("hello world!");
def capitalize_every_word(s):
  return s.title()

capitalize_every_word('hello world!') # 'Hello World!'
  1. Fatal error: Uncaught ArgumentCountError: Too few arguments to function capitalize(), 1 passed on line 10
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");

function capitalize($s, $lower_rest) {
    return PyCore::str("")->join(new PyList([$s->__getitem__(PyCore::slice(null, 1, null))->upper(), $lower_rest ? $s->__getitem__(PyCore::slice(1, null, null))->lower() : $s->__getitem__(PyCore::slice(1, null, null))]));
}


capitalize("fooBar");
capitalize("fooBar", true);
def capitalize(s, lower_rest = False):
  return ''.join([s[:1].upper(), (s[1:].lower() if lower_rest else s[1:])])

capitalize('fooBar') # 'FooBar'
capitalize('fooBar', True) # 'Foobar'
  1. Warning: Undefined variable $tuple on line 6
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");

function cast_list($val) {
    return PyCore::isinstance($val, [$tuple, $list, $set, $dict]) ? PyCore::list($val) : new PyList([$val]);
}


cast_list("foo");
cast_list(new PyList([1]));
cast_list(["foo", "bar"]);
def cast_list(val):
  return list(val) if isinstance(val, (tuple, list, set, dict)) else [val]

cast_list('foo') # ['foo']
cast_list([1]) # [1]
cast_list(('foo', 'bar')) # ['foo', 'bar']
  1. Parse error: syntax error, unexpected variable "$lst", expecting ":" on line 8
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");
$ceil = PyCore::import('math')->ceil;

function chunk_into_n($lst, $n) {
    $size = $ceil(PyCore::len($lst) / $n);
    return PyCore::list(PyCore::map(return $lst->__getitem__(PyCore::slice($x * $size, $x * $size + $size, null));

    }, PyCore::list(PyCore::range($n))));
}


chunk_into_n(new PyList([1, 2, 3, 4, 5, 6, 7]), 4);
from math import ceil

def chunk_into_n(lst, n):
  size = ceil(len(lst) / n)
  return list(
    map(lambda x: lst[x * size:x * size + size],
    list(range(n)))
  )

chunk_into_n([1, 2, 3, 4, 5, 6, 7], 4) # [[1, 2], [3, 4], [5, 6], [7]]
  1. Parse error: syntax error, unexpected token "!", expecting ":" on line 72
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");

function difference($a, $b) {
    return (function($a) {
        $___ = [];
        $elts = [];
        foreach($a as $item) {
            $elts[0] = $item;
        }
        $___[] = $elts;
        return $___;
    })($a);
}



function difference($a, $b) {
    return (function($a) {
        $___ = [];
        $elts = [];
        foreach($a as $item) {
            $elts[0] = $item;
        }
        $___[] = $elts;
        return $___;
    })($a);
}



function difference($a, $b) {
    return (function($a) {
        $___ = [];
        $elts = [];
        foreach($a as $item) {
            $elts[0] = $item;
        }
        $___[] = $elts;
        return $___;
    })($a);
}



function make_set($itr) {
    PyCore::print("Making set...");
    return PyCore::set($itr);
}


PyCore::print(difference(new PyList([1, 2, 3]), new PyList([1, 2, 4])));

function difference($a, $b) {
    $_b = PyCore::set($b);
    return (function($a) {
        $___ = [];
        $elts = [];
        foreach($a as $item) {
            $elts[0] = $item;
        }
        $___[] = $elts;
        return $___;
    })($a);
}



function difference($a, $b) {
    $_b = PyCore::set($b);
    return PyCore::list(PyCore::filter(return !$_b->__contains__($item)

    }, $a));
}

def difference(a, b):
  return [item for item in a if item not in b]



def difference(a, b):
  return [item for item in a if item not in set(b)]



def difference(a, b):
  return [item for item in a if item not in make_set(b)]

def make_set(itr):
  print('Making set...')
  return set(itr)

print(difference([1, 2, 3], [1, 2, 4]))
# Making set...
# Making set...
# Making set...
# [3]



def difference(a, b):
  _b = set(b)
  return [item for item in a if item not in _b]



def difference(a, b):
  _b = set(b)
  return list(filter(lambda item: item not in _b, a))
  1. Fatal error: Cannot redeclare compact() on line 6
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");

function compact($lst) {
    return PyCore::list(PyCore::filter(null, $lst));
}


compact(new PyList([0, 1, false, 2, "", 3, "a", "s", 34]));
def compact(lst):
  return list(filter(None, lst))

compact([0, 1, False, 2, '', 3, 'a', 's', 34]) # [ 1, 2, 3, 'a', 's', 34 ]
  1. Fatal error: Can't use method return value in write context on line 12
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");
$defaultdict = PyCore::import('collections')->defaultdict;
$floor = PyCore::import('math')->floor;

function count_by($lst, $fn) {
    $count = $defaultdict($int);
    $__iter = PyCore::iter(PyCore::map($fn, $lst));
    while($current = PyCore::next($__iter)) {
        $val = $current;
        $countval->__setitem__($val, $__value) += 1;    
    }
    return PyCore::dict($count);
}


count_by(new PyList([6.1, 4.2, 6.3]), $floor);
count_by(new PyList(["one", "two", "three"]), $len);
from collections import defaultdict
from math import floor

def count_by(lst, fn = lambda x: x):
  count = defaultdict(int)
  for val in map(fn, lst):
    count[val] += 1
  return dict(count)

count_by([6.1, 4.2, 6.3], floor) # {6: 2, 4: 1}
count_by(['one', 'two', 'three'], len) # {3: 2, 5: 1}
  1. Fatal error: Uncaught PyError: ('min',) in /home/mrpzx/git/phpy/phpy-examples/30s/python-code/dict-getkey-vs-dictkey.php:14
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");
$a = new PyDict([
    "max" => 200,
]);
$b = new PyDict([
    "min" => 100,
    "max" => 250,
]);
$c = new PyDict([
    "min" => 50,
]);
$a->__getitem__("min") + $b->__getitem__("min") + $c->__getitem__("min");
$a->get("min", 0) + $b->get("min", 0) + $c->get("min", 0);
a = { 'max': 200 }
b = { 'min': 100, 'max': 250 }
c = { 'min': 50 }

a['min'] + b['min'] + c['min'] # throws KeyError
a.get('min', 0) + b.get('min', 0) + c.get('min', 0) # 150
  1. Warning: Undefined variable $int on line 6
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");

function digitize($n) {
    return PyCore::list(PyCore::map($int, PyCore::str($n)));
}


digitize(123);
def digitize(n):
  return list(map(int, str(n)))

digitize(123) # [1, 2, 3]
  1. Warning: Undefined variable $print on line 14
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");

function for_each($itr, $fn) {
    $__iter = PyCore::iter($itr);
    while($current = PyCore::next($__iter)) {
        $el = $current;
        $fn($el);    
    }
}


for_each(new PyList([1, 2, 3]), $print);
def for_each(itr, fn):
  for el in itr:
    fn(el)

for_each([1, 2, 3], print) # 1 2 3
  1. Fatal error: Uncaught TypeError: Unsupported operand types: PyList + PyList on line 6
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");

function have_same_contents($a, $b) {
    $__iter = PyCore::iter(PyCore::set($a + $b));
    while($current = PyCore::next($__iter)) {
        $v = $current;
        if ($a->count($v) != $b->count($v)) {
            return false;
        }
    
    }
    return true;
}


have_same_contents(new PyList([1, 2, 4]), new PyList([2, 4, 1]));
def have_same_contents(a, b):
  for v in set(a + b):
    if a.count(v) != b.count(v):
      return False
  return True

have_same_contents([1, 2, 4], [2, 4, 1]) # True
  1. Error: Unsupported Python Syntax, Line: 3, Type: GeneratorExp
Error: Unsupported Python Syntax, Line: 3, Type: GeneratorExp
def hex_to_rgb(hex):
  return tuple(int(hex[i:i+2], 16) for i in (0, 2, 4))

hex_to_rgb('FFA501') # (255, 165, 1)
  1. Parse error: syntax error, unexpected double-quoted string " ", expecting ":" on line 7
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");
$sub = PyCore::import('re')->sub;

function kebab($s) {
    return PyCore::str("-")->join($sub("(\\s|_|-)+", " ", $sub("[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+", return " " + $mo->group(0)->lower();

    }, $s))->split());
}


kebab("camelCase");
kebab("some text");
kebab("some-mixed_string With spaces_underscores-and-hyphens");
kebab("AllThe-small Things");
from re import sub

def kebab(s):
  return '-'.join(
    sub(r"(\s|_|-)+"," ",
    sub(r"[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+",
    lambda mo: ' ' + mo.group(0).lower(), s)).split())

kebab('camelCase') # 'camel-case'
kebab('some text') # 'some-text'
kebab('some-mixed_string With spaces_underscores-and-hyphens')
# 'some-mixed-string-with-spaces-underscores-and-hyphens'
kebab('AllThe-small Things') # 'all-the-small-things'
  1. Fatal error: Uncaught TypeError: Unsupported operand types: PyObject - int in /home/mrpzx/git/phpy/phpy-examples/30s/python-code/median.php:9
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");

function median($list) {
    $list->sort();
    $list_length = PyCore::len($list);
    if ($list_length % 2 == 0) {
        return $list->__getitem__(PyCore::int($list_length / 2) - 1) + $list->__getitem__(PyCore::int($list_length / 2)) / 2;
    }

    return PyCore::float($list->__getitem__(PyCore::int($list_length / 2)));
}


median(new PyList([1, 2, 3]));
median(new PyList([1, 2, 3, 4]));
def median(list):
  list.sort()
  list_length = len(list)
  if list_length % 2 == 0:
    return (list[int(list_length / 2) - 1] + list[int(list_length / 2)]) / 2
  return float(list[int(list_length / 2)])

median([1, 2, 3]) # 2.0
median([1, 2, 3, 4]) # 2.5
  1. Fatal error: Cannot redeclare append() (previously declared on line 20
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");

function append($n, $l) {
    $l->append($n);
    return $l;
}


append(0);
append(1);

function append($n, $l) {
    if ($l == null) {
        $l = new PyList([]);
    }

    $l->append($n);
    return $l;
}


append(0);
append(1);
def append(n, l = []):
  l.append(n)
  return l

append(0) # [0]
append(1) # [0, 1]



def append(n, l = None):
  if l is None:
    l = []
  l.append(n)
  return l

append(0) # [0]
append(1) # [1]
  1. Fatal error: Uncaught TypeError: Unsupported operand types: string * int in /home/mrpzx/git/phpy/phpy-examples/30s/python-code/n-times-string.php:6
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");

function n_times_string($s, $n) {
    return $s * $n;
}


n_times_string("py", 4);
def n_times_string(s, n):
  return (s * n)

n_times_string('py', 4) #'pypypypy'
  1. Fatal error: Uncaught TypeError: Unsupported operand types: PyObject / PyObject in /home/mrpzx/git/phpy/phpy-examples/30s/python-code/num-to-range.php:6
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");

function num_to_range($num, $inMin, $inMax, $outMin, $outMax) {
    return $outMin + PyCore::float($num - $inMin) / PyCore::float($inMax - $inMin) * $outMax - $outMin;
}


num_to_range(5, 0, 10, 0, 100);
def num_to_range(num, inMin, inMax, outMin, outMax):
  return outMin + (float(num - inMin) / float(inMax - inMin) * (outMax
                  - outMin))

num_to_range(5, 0, 10, 0, 100) # 50.0
  1. Fatal error: Uncaught Error: Call to a member function getitem() on string in /home/mrpzx/git/phpy/phpy-examples/30s/python-code/reverse.php:6
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");

function reverse($itr) {
    return $itr->__getitem__(PyCore::slice(null, null, -1));
}


reverse(new PyList([1, 2, 3]));
reverse("snippet");
def reverse(itr):
  return itr[::-1]

reverse([1, 2, 3]) # [3, 2, 1]
reverse('snippet') # 'teppins'
  1. Fatal error: Uncaught PyError: attempt to assign sequence of size 3 to extended slice of size 2 in /home/mrpzx/git/phpy/phpy-examples/30s/python-code/slice-assignment.php:31
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");
$nums = new PyList([1, 2, 3, 4, 5]);
$__value = new PyList([6]);
$nums->__setitem__(PyCore::slice(null, 1, null), $__value);
$__value = new PyList([7, 8]);
$nums->__setitem__(PyCore::slice(1, 3, null), $__value);
$__value = new PyList([9, 0]);
$nums->__setitem__(PyCore::slice(-2, null, null), $__value);
$nums = new PyList([1, 2, 3, 4, 5]);
$__value = new PyList([6, 7]);
$nums->__setitem__(PyCore::slice(1, 4, null), $__value);
$__value = new PyList([8, 9, 0]);
$nums->__setitem__(PyCore::slice(-1, null, null), $__value);
$__value = new PyList([]);
$nums->__setitem__(PyCore::slice(null, 1, null), $__value);
$nums = new PyList([1, 2, 3, 4, 5]);
$__value = new PyList([6, 7]);
$nums->__setitem__(PyCore::slice(2, 2, null), $__value);
$__value = new PyList([8, 9]);
$nums->__setitem__(PyCore::slice(7, null, null), $__value);
$__value = new PyList([0]);
$nums->__setitem__(PyCore::slice(null, 0, null), $__value);
$__value = new PyList([4, 2]);
$nums->__setitem__(PyCore::slice(null, null, null), $__value);
$nums = new PyList([1, 2, 3, 4, 5]);
$__value = new PyList([6, 7]);
$nums->__setitem__(PyCore::slice(2, 5, 2), $__value);
$__value = new PyList([6, 7, 8]);
$nums->__setitem__(PyCore::slice(2, 5, 2), $__value);
$__value = new PyList([9, 0]);
$nums->__setitem__(PyCore::slice(1, null, -1), $__value);
nums = [1, 2, 3, 4, 5]

nums[:1] = [6]        # [6, 2, 3, 4, 5]   (replace elements 0 through 1)
nums[1:3] = [7, 8]    # [6, 7, 8, 4, 5]   (replace elements 1 through 3)
nums[-2:] = [9, 0]    # [6, 7, 8, 9, 0]   (replace the last 2 elements)

nums = [1, 2, 3, 4, 5]

nums[1:4] = [6, 7]    # [1, 6, 7, 5]        (replace 3 elements with 2)
nums[-1:] = [8, 9, 0] # [1, 6, 7, 8, 9, 0]  (replace 1 element with 3)
nums[:1] = []         # [6, 7, 8, 9, 0]     (replace 1 element with 0)

nums = [1, 2, 3, 4, 5]

nums[2:2] = [6, 7]    # [1, 2, 6, 7, 3, 4, 5]   (insert 2 elements)
nums[7:] = [8, 9]     # [1, 2, 6, 7, 3, 4, 5, 8, 9] (append 2 elements)
nums[:0] = [0]        # [0, 1, 2, 6, 7, 3, 4, 5, 8, 9] (prepend 1 element)
nums[:] = [ 4, 2]     # [4, 2]         (replace whole list with a new one)

nums = [1, 2, 3, 4, 5]

nums[2:5:2] = [6, 7]  # [1, 2, 6, 4, 7] (replace every 2nd element, 2 through 5)
nums[2:5:2] = [6, 7, 8] # Throws a ValueError (can't replace 2 elements with 3)
nums[1::-1] = [9, 0]  # [0, 9, 6, 4, 7] (reverse replace, 1 through start)
  1. Fatal error: Uncaught Error: Call to a member function lower() on string in /home/mrpzx/git/phpy/phpy-examples/30s/python-code/slugify.php:7
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");
$re = PyCore::import('re');

function slugify($s) {
    $s = $s->lower()->strip();
    $s = $re->sub("[^\\w\\s-]", "", $s);
    $s = $re->sub("[\\s_-]+", "-", $s);
    $s = $re->sub("^-+|-+$", "", $s);
    return $s;
}


slugify("Hello World!");
import re

def slugify(s):
  s = s.lower().strip()
  s = re.sub(r'[^\w\s-]', '', s)
  s = re.sub(r'[\s_-]+', '-', s)
  s = re.sub(r'^-+|-+$', '', s)
  return s

slugify('Hello World!') # 'hello-world'
  1. Parse error: syntax error, unexpected token "return" on line 6
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");

function sort_dict_by_value($d, $reverse) {
    return PyCore::dict(PyCore::sorted($d->items(), key: return $x->__getitem__(1);

    }, reverse: $reverse));
}


$d = new PyDict([
    "one" => 1,
    "three" => 3,
    "five" => 5,
    "two" => 2,
    "four" => 4,
]);
sort_dict_by_value($d);
sort_dict_by_value($d, true);
def sort_dict_by_value(d, reverse = False):
  return dict(sorted(d.items(), key = lambda x: x[1], reverse = reverse))

d = {'one': 1, 'three': 3, 'five': 5, 'two': 2, 'four': 4}
sort_dict_by_value(d) # {'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5}
sort_dict_by_value(d, True)
# {'five': 5, 'four': 4, 'three': 3, 'two': 2, 'one': 1}
  1. Fatal error: Uncaught Error: Call to a member function split() on string in /home/mrpzx/git/phpy/phpy-examples/30s/python-code/split-lines.php:6
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");

function split_lines($s) {
    return $s->split("\n");
}


split_lines("This\nis a\nmultiline\nstring.\n");
def split_lines(s):
  return s.split('\n')

split_lines('This\nis a\nmultiline\nstring.\n')
# ['This', 'is a', 'multiline', 'string.' , '']
  1. Fatal error: Uncaught TypeError: Unsupported operand types: PyStr * int in /home/mrpzx/git/phpy/phpy-examples/30s/python-code/to-roman-numeral.php:12
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");

function to_roman_numeral($num) {
    $lookup = new PyList([[1000, "M"], [900, "CM"], [500, "D"], [400, "CD"], [100, "C"], [90, "XC"], [50, "L"], [40, "XL"], [10, "X"], [9, "IX"], [5, "V"], [4, "IV"], [1, "I"]]);
    $res = "";
    $__iter = PyCore::iter($lookup);
    while($current = PyCore::next($__iter)) {
        [$n, $roman] = $current;
        [$d, $num] = PyCore::divmod($num, $n);
        $res += $roman * $d;    
    }
    return $res;
}


to_roman_numeral(3);
to_roman_numeral(11);
to_roman_numeral(1998);
def to_roman_numeral(num):
  lookup = [
    (1000, 'M'),
    (900, 'CM'),
    (500, 'D'),
    (400, 'CD'),
    (100, 'C'),
    (90, 'XC'),
    (50, 'L'),
    (40, 'XL'),
    (10, 'X'),
    (9, 'IX'),
    (5, 'V'),
    (4, 'IV'),
    (1, 'I'),
  ]
  res = ''
  for (n, roman) in lookup:
    (d, num) = divmod(num, n)
    res += roman * d
  return res

to_roman_numeral(3) # 'III'
to_roman_numeral(11) # 'XI'
to_roman_numeral(1998) # 'MCMXCVIII'
  1. Fatal error: Uncaught TypeError: Unsupported operand types: PyList + array in /home/mrpzx/git/phpy/phpy-examples/30s/python-code/union-by.php:15
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");

function union_by($a, $b, $fn) {
    $_a = PyCore::set(PyCore::map($fn, $a));
    return PyCore::list(PyCore::set($a + (function($b) {
        $___ = [];
        $elts = [];
        foreach($b as $item) {
            $elts[0] = $item;
        }
        $___[] = $elts;
        return $___;
    })($b)));
}


$floor = PyCore::import('math')->floor;
union_by(new PyList([2.1]), new PyList([1.2, 2.3]), $floor);
def union_by(a, b, fn):
  _a = set(map(fn, a))
  return list(set(a + [item for item in b if fn(item) not in _a]))

from math import floor

union_by([2.1], [1.2, 2.3], floor) # [2.1, 1.2]

以上python代码来自 https://github.com/Chalarangelo/30-seconds-of-code/tree/master/content/snippets/python/s,相关提取、转换、测试代码如下

  • md.php
<?php

$files = glob('python-md/*.md');
array_map(function ($file) {
    preg_match_all('/```py([\s\S]+?)```/', file_get_contents($file), $matches);
    file_put_contents(substr($file, 0, -3) . '.py', implode("\n\n", $matches[1]));
}, $files);
  • py.php
<?php

$files = glob('python-code/*.py');
array_map(function ($file) {
    file_put_contents(substr($file, 0, -3) . '.php', convert(file_get_contents($file)));
}, $files);

function convert($code)
{
    return json_decode(request('https://swoole.com/py2php/convert.php', 'POST', ['code' => $code])[0])?->data?->code;
}

/**
 * http request
 * from swoole/ide-helper
 * @param string $url 
 * @param string $method 
 * @param mixed $data 
 * @param array $options 
 * @param null|array $headers 
 * @param null|array $cookies
 * @return array 
 */
function request(
    string $url,
    string $method = 'GET',
    $data = null,
    array $options = ['timeout' => 30],
    array $headers = null,
    array $cookies = null
): array {
    $ch = curl_init($url);
    if (empty($ch)) {
        throw new \Exception('failed to curl_init');
    }
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($method));
    if ($data) {
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    }
    if ($headers) {
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    }
    if ($cookies) {
        $cookie_str = '';
        foreach ($cookies as $k => $v) {
            $cookie_str .= "{$k}={$v}; ";
        }
        curl_setopt($ch, CURLOPT_COOKIE, $cookie_str);
    }
    if (isset($options['timeout'])) {
        if (is_float($options['timeout'])) {
            curl_setopt($ch, CURLOPT_TIMEOUT_MS, intval($options['timeout'] * 1000));
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, intval($options['timeout'] * 1000));
        } else {
            curl_setopt($ch, CURLOPT_TIMEOUT, intval($options['timeout']));
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, intval($options['timeout']));
        }
    }
    if (isset($options['connect_timeout'])) {
        if (is_float($options['connect_timeout'])) {
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, intval($options['connect_timeout'] * 1000));
        } else {
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, intval($options['connect_timeout']));
        }
    }
    if (isset($options['verify'])) {
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); // 对认证证书来源的检查
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); // 从证书中检查SSL加密算法是否存在
        // curl_setopt($ch, CURLOPT_SSLVERSION, 2);//设置SSL协议版本号
    }
    $body = curl_exec($ch);
    if ($body !== false) {
        return [$body, curl_getinfo($ch, CURLINFO_HTTP_CODE)];
    }
    throw new \Exception(curl_error($ch), curl_errno($ch));
}
  • test.php
<?php

$files = glob('python-code/*.php');

$i = 0;
$errs = [];
array_map(function ($file) use (&$i, &$errs) {
    $proc = proc_open('php ' . $file, [0 => STDIN, 1 => ['pipe', 'w'], 2 => ['redirect', 1]], $pipes);
    $stdout = stream_get_contents($pipes[1]);
    $status = proc_get_status($proc);
    proc_close($proc);

    if ($status['exitcode']) {
        $msg = explode("\n", trim($stdout))[0];
        if (!preg_match('/(.+?)in \/home.+?\s*(on line \d+)/', $msg, $match)) {
            $match = ['', $msg];
        }
        if (isset($errs[$match[1]])) {
            return;
        }
        $errs[$match[1]] = 0;
        echo (++$i) . '. ', $match[1], ' ', $match[2] ?? '', PHP_EOL;

        $pycode = file_get_contents(substr($file, 0, -4) . '.py');
        $phpcode = file_get_contents($file);
        echo <<<CODE
\```php
$phpcode
\```
CODE;
        echo PHP_EOL;
        echo <<<CODE
\```python
$pycode
\```
CODE;
        echo PHP_EOL;
    }
}, $files);
\```

php8.3编译报错sapi/embed/php_embed.h: No such file or directory

/root/src/phpy/src/python/module.cc:19:10: fatal error: sapi/embed/php_embed.h: No such file or directory 19 | #include <sapi/embed/php_embed.h> | ^~~~~~~~~~~~~~~~~~~~~~~~ compilation terminated. make: *** [Makefile:252: src/python/module.lo] Error 1
把PHP改成8.3之后,还有这个报错

安装扩展报错

报错信息如下

/usr/src/php/ext/phpy/src/bridge/core.cc:259:9: error: 'zend_array_is_list' was not declared in this scope; did you mean 'zend_ast_is_list'?
259 | if (zend_array_is_list(ht)) {
| ^~~~~~~~~~~~~~~~~~
| zend_ast_is_list
/usr/src/php/ext/phpy/src/bridge/core.cc:258:17: warning: control reaches end of non-void function [-Wreturn-type]
258 | zend_array *ht = Z_ARRVAL_P(zv);
| ^~
make: *** [Makefile:194: src/bridge/core.lo] Error 1`
不知道该怎么解决呢

编译成功了 但是python import报错

需要额外扩展设置吗

./configure --with-php-config=/usr/bin/php-config8.3 --with-python-config=/usr/bin/python3.10-config --with-python-version=3.10


Python 3.10.13 (main, Sep 11 2023, 13:44:35) [GCC 11.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import phpy
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: /home/wxy/phpy/modules/phpy.so: undefined symbol: php_stream_memory_ops
>>>

image

make install报错

g++ -I. -I/root/inst/phpy -I/root/inst/phpy/include -I/root/inst/phpy/main -I/root/inst/phpy -I/www/server/php/81/include/php -I/www/server/php/81/include/php/main -I/www/server/php/81/include/php/TSRM -I/www/server/php/81/include/php/Zend -I/www/server/php/81/include/php/ext -I/www/server/php/81/include/php/ext/date/lib -I/opt/conda/include/python3.11 -DHAVE_CONFIG_H -g -O2 -Wall -Wno-unused-function -Wno-deprecated -Wno-deprecated-declarations -z now -std=c++14 -DZEND_COMPILE_DL_EXT=1 -c /root/inst/phpy/src/python/class.cc -MMD -MF src/python/class.dep -MT src/python/class.lo -fPIC -DPIC -o src/python/.libs/class.o
/root/inst/phpy/src/python/class.cc:50:1: sorry, unimplemented: non-trivial designated initializers not supported
};
^
/root/inst/phpy/src/python/class.cc:50:1: sorry, unimplemented: non-trivial designated initializers not supported
/root/inst/phpy/src/python/class.cc:50:1: sorry, unimplemented: non-trivial designated initializers not supported
/root/inst/phpy/src/python/class.cc:50:1: sorry, unimplemented: non-trivial designated initializers not supported
/root/inst/phpy/src/python/class.cc:50:1: sorry, unimplemented: non-trivial designated initializers not supported
Makefile:241: recipe for target 'src/python/class.lo' failed
make: *** [src/python/class.lo] Error 1

alpine下存在php模块加载错误

Dockerfile:

FROM php:8.2-fpm-alpine

WORKDIR /var/www

RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
    apk update && \
    apk add python3 python3-dev git m4 autoconf gcc g++ make && \
    git clone https://github.com/swoole/phpy.git /var/phpy && \
    cd /var/phpy && \
    docker-php-source extract && \
    phpize && \
    ./configure --with-python-dir=/usr && \
    make && make install && \
    docker-php-ext-enable phpy && \
    docker-php-source delete && \
    apk del git m4 autoconf gcc g++ make && \
    rm -rf /var/phpy

VOLUME /var/www

构建完成后报错

/var/phpy # php --ini

Warning: PHP Startup: Unable to load dynamic library 'phpy' (tried: /usr/local/lib/php/extensions/no-debug-non-zts-20210902/phpy (Error loading shared library /usr/local/lib/php/extensions/no-debug-non-zts-20210902/phpy: No such file or directory), /usr/local/lib/php/extensions/no-debug-non-zts-20210902/phpy.so (Error relocating /usr/local/lib/php/extensions/no-debug-non-zts-20210902/phpy.so: _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_disposeEv: symbol not found)) in Unknown on line 0

PY 整数如何执行除法

根据文档推测除法函数为div,但是这里会抛出异常,PHP Fatal error: Uncaught PyError: 'int' object has no attribute '__div__'

PyCore::int(2)->__div__(2); 

此外还有一个问题:如果需要大量的计算时,使用bcmath性能更好还是使用python的整数更优。

另外PY整数能支持链式调用就好啦,例如: PyCore::int(2)->__add__(1)->__mul__(2);

phpy错误的把python的空字典转换成了php的空数组

复现代码

$function = function($dict) {
    echo PyCore::type($dict), PHP_EOL;
};
$globals = new PyDict(['function' => $function]);
PyCore::exec('function({})', $globals);

结果

$ php php_test.php
<class 'list'>

经调试发现问题出在bridge/core.cc:88

if (PyDict_Check(pv)) {
      dict2array(pv, zv);
  }

当pv是空字典{}时转成了php的空数组[],再经过array2py转换后就变成了python的list,解决方案如下:

if (PyDict_Check(pv)) {
        if (PyDict_Size(pv)) {
            dict2array(pv, zv);
        } else {
            phpy::php::new_dict(zv, pv);
        }
    }

修改后再次测试复现代码,结果如下

$ php php_test.php
<class 'dict'>

修复后可实现在 php中继承python的类,示例如下

  • py_test.py
import abc

class Animal(abc.ABC):
    def __init__(self, name, age):
       self.name = name
       self.age = age

    @abc.abstractmethod
    def speak(self, name):
        pass
  • php_test.php
<?php

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

$Animal = PyCore::import('py_test')->Animal;
$Dog = PyCore::import('types')->new_class(
    'Dog',
    (PyCore::tuple([$Animal])),
    [],
    function ($ns) {
        $ns['speak'] = function ($self) {
            PyCore::print("My name is {$self->name}, age is {$self->age}");
        };
    }
);

$dog = $Dog('', 1);
$dog->speak($dog);
echo PyCore::type($dog), PHP_EOL;

运行结果符合预期

$ php php_test.php 
My name is 狗, age is 1
<class 'abc.Dog'>

这个继承类的方案还有一个大坑没解决,function ($ns) 里面丢失了 self,也无法使用 PyCore::super(),暂时只能玩一下

PyList能否直接转化成php的数组

目前通过offset可以直接访问,但是好像没法直接转化成数组(或者说转化成数组后里面的值是空的)

示例:

// 可以转成array 但是里面的值是空的
$a = new \PyList([1,2,3]);
PyCore::print(PyCore::type($a));  // output: <class 'list'>
PyCore::print($a);  // output: [1, 2, 3]
PyCore::print($a[0]);   // output: 1
var_dump($a);  // output: object(PyList)#1 (0) {}
var_dump($a[1]); // output: int(2)
var_dump((array) $a);  //  output: array(0) {}


// ndarray 虽然也可以转成array 但是里面的值依然是空的

$np = PyCore::import("numpy");

$b = $np->array([1,2,3]);               
PyCore::print(PyCore::type($b));  // output: <class 'numpy.ndarray'>
PyCore::print($b);   // output: [1 2 3]
PyCore::print($b[0]);  // output: 1
var_dump($b);   // output: object(PyObject)#3 (0) {}
var_dump($b[1]); // output: object(PyObject)#4 (0) {}
var_dump((array) $b);  //  output: array(0) {}

mac m1芯片是否支持安装?

我安装后php错误日志中出现错误,错误信息为:
[02-Aug-2024 08:04:51 UTC] PHP Warning: PHP Startup: Unable to load dynamic library 'phpy.so' (tried: /Applications/MAMP/bin/php/php8.1.0/lib/php/extensions/no-debug-non-zts-20210902/phpy.so (dlopen(/Applications/MAMP/bin/php/php8.1.0/lib/php/extensions/no-debug-non-zts-20210902/phpy.so, 0x0009): tried: '/Applications/MAMP/bin/php/php8.1.0/lib/php/extensions/no-debug-non-zts-20210902/phpy.so' (mach-o file, but is an incompatible architecture (have 'arm64', need 'x86_64')), '/usr/local/lib/phpy.so' (no such file), '/usr/lib/phpy.so' (no such file)), /Applications/MAMP/bin/php/php8.1.0/lib/php/extensions/no-debug-non-zts-20210902/phpy.so.so (dlopen(/Applications/MAMP/bin/php/php8.1.0/lib/php/extensions/no-debug-non-zts-20210902/phpy.so.so, 0x0009): tried: '/Applications/MAMP/bin/php/php8.1.0/lib/php/extensions/no-debug-non-zts-20210902/phpy.so.so' (no such file), '/usr/local/lib/phpy.so.so' (no such file), '/usr/lib/phpy.so.so' (no such file))) in Unknown on line 0

加载phpy扩展失败

在php:8.1.27-fpm-alpine容器中

  1. 编译完成执行docker-php-ext-enable phpy后 会生成docker-php-ext-phpy.ini文件内容是extension=phpy
    执行php --ri phpy会报错:
    Warning: PHP Startup: Unable to load dynamic library 'phpy' (tried: /usr/local/lib/php/extensions/no-debug-non-zts-20210902/phpy (Error loading shared library /usr/local/lib/php/extensions/no-debug-non-zts-20210902/phpy: No such file or directory), /usr/local/lib/php/extensions/no-debug-non-zts-20210902/phpy.so (Error relocating /usr/local/lib/php/extensions/no-debug-non-zts-20210902/phpy.so: _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_disposeEv: symbol not found)) in Unknown on line 0
    2.手动更改docker-php-ext-phpy.ini文件内容为extension=phpy.so
    执行php --ri phpy会报错:
    Warning: PHP Startup: Unable to load dynamic library 'phpy.so' (tried: /usr/local/lib/php/extensions/no-debug-non-zts-20210902/phpy.so (Error relocating /usr/local/lib/php/extensions/no-debug-non-zts-20210902/phpy.so: _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_disposeEv: symbol not found), /usr/local/lib/php/extensions/no-debug-non-zts-20210902/phpy.so.so (Error loading shared library /usr/local/lib/php/extensions/no-debug-non-zts-20210902/phpy.so.so: No such file or directory)) in Unknown on line 0
    Extension 'phpy' not present.

docker内的liunx的python安装方式,能来个详细点的教程吗

docker容器内的 Alpine Linux 系统的python安装方式,能来个详细点的教程吗,看的无从下手的样子,我用的是apk add python3命令一键安装的 ,安装路径是/usr/bin/python3,之后:./configure --with-python-dir=/usr/bin/python3,make一直报错
image
image

PHP-FPM中如何使用?

编译成功后,在/etc/php/8.2/fpm/php.ini里添加了extension=phpy,然后 systemctl restart php8.2-fpm 查看phpinfo()仍然没出现。cli/php.ini里加了后,php -m里马上就看到了

Python 对象内存泄露问题

Python 对象回收与内存释放似乎有些问题, 还是我的使用方法不对? 大佬帮忙看看
比较奇怪的是opencv读取图片返回的也是ndarray,但是只要不调用别的方法内存就不会增长

<?php

ini_set('memory_limit', '1G');


class a {
    public function __construct(
        public array $a
    ){}
}

// 内存不会持续增长
function test(){
    $arr = [];

    for ($i=0; $i < 100000; $i++) { 
        $arr[] = $i;
    }

    $a = new a($arr);
}

// 内存会持续增长
function test1(){
    $arr = [];

    for ($i=0; $i < 100000; $i++) { 
        $arr[] = $i;
    }

    $a = new PyList($arr);

    $a = null;
    unset($a);
}

// 只读取图片,内存不会持续增长
// 一旦调用方法就会增长
function test2() {
    $tf = PyCore::import("tensorflow");
    $interpreter = $tf->lite->Interpreter('./yolov8s_float32.tflite');

    $cv = PyCore::import("cv2");
    $image = $cv->imread('./IMG_20240522_213146.jpg');  // 仅此不会增长

    //PyCore::type($image);             // 会内存增长
    //$cv->resize($image, [640, 640]);  // 会内存增长
}

// 内存会持续增长
function test3(){
    $cv = PyCore::import("numpy");
    
    $arr = [];

    for ($i=0; $i < 100000; $i++) { 
        $arr[] = $i;
    }

    $a = $cv->array($arr);
}


var_dump(memory_get_usage(true));
var_dump(memory_get_peak_usage(true));

for ($i=0; $i < 10000000; $i++) { 
    test1($i);

    var_dump("finished $i");

    // 这里内存保持稳定,因此应该是python那边内存没有释放
    // 猜测php这边传递过去的py类,在php这边计数为零后,在py那边没有销毁
    var_dump(memory_get_usage(true));
    var_dump(memory_get_peak_usage(true));

    usleep(100000);
}

var_dump(memory_get_usage(true));
var_dump(memory_get_peak_usage(true));

sleep(100);

环境信息
目前测试过 py 3.11和3.12 phpy 1.0.4和1.0.5 均存在这个问题

python3 --version
Python 3.11.9
php --version    
PHP 8.3.6 (cli) (built: Apr 11 2024 20:23:58) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.6, Copyright (c) Zend Technologies
    with Zend OPcache v8.3.6, Copyright (c), by Zend Technologies
php --ri phpy 

phpy

phpy support => enabled
Copyright => 上海识沃网络科技有限公司
Email => [email protected]
Website => https://www.swoole.com/
Version => 1.0.5
php --ri swoole

swoole

Swoole => enabled
Author => Swoole Team <[email protected]>
Version => 5.1.2
Built => May 14 2024 11:32:05
coroutine => enabled with boost asm context
epoll => enabled
eventfd => enabled
signalfd => enabled
cpu_affinity => enabled
spinlock => enabled
rwlock => enabled
openssl => OpenSSL 3.0.13 30 Jan 2024
dtls => enabled
http2 => enabled
json => enabled
curl-native => enabled
c-ares => 1.27.0
zlib => 1.3
mutex_timedlock => enabled
pthread_barrier => enabled
futex => enabled
async_redis => enabled
coroutine_pgsql => enabled

Directive => Local Value => Master Value
swoole.enable_coroutine => On => On
swoole.enable_library => On => On
swoole.enable_fiber_mock => Off => Off
swoole.enable_preemptive_scheduler => Off => Off
swoole.display_errors => On => On
swoole.use_shortname => Off => Off
swoole.unixsock_buffer_size => 8388608 => 8388608

我自己构建的phpy.so加载报错

``
PHP : /usr/local/bin/php
Warning: PHP Startup: Unable to load dynamic library 'phpy' (tried: /usr/local/lib/php/extensions/no-debug-non-zts-20220829/phpy (Error loading shared library /usr/local/lib/php/extensions/no-debug-non-zts-20220829/phpy: No such file or directory), /usr/local/lib/php/extensions/no-debug-non-zts-20220829/phpy.so (Error relocating /usr/local/lib/php/extensions/no-debug-non-zts-20220829/phpy.so: _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_disposeEv: symbol not found)) in Unknown on line 0
`

PyCore::fn改了参数的类型

复现代码

<?php

$pycore = <<<CODE
from modelscope.utils.config import Config

cfg = Config.from_string('''
{
    "framework": "pytorch",
    "task": "ocr-recognition"
}
''', file_format='.json')

def test_py():
    return print(type(cfg))

def test_php(cb):
    return cb(cfg)
CODE;

$globals = new PyDict;
PyCore::exec($pycore, $globals);

$globals['test_py']();
$globals['test_php'](PyCore::fn(function ($cfg) {
    PyCore::print(PyCore::type($cfg));
}));

执行结果

$ php test.php
2023-12-18 11:19:05,821 - modelscope - INFO - PyTorch version 2.0.1 Found.
2023-12-18 11:19:05,821 - modelscope - INFO - TensorFlow version 2.15.0.post1 Found.
2023-12-18 11:19:05,821 - modelscope - INFO - Loading ast index from /home/mrpzx/.cache/modelscope/ast_indexer
2023-12-18 11:19:05,861 - modelscope - INFO - Loading done! Current index file version is 1.10.0, with md5 238e3ec6956bd434f67275fea2be7f2b and a total number of 946 components indexed
<class 'modelscope.utils.config.Config'>
<class 'list'>

测试https://modelscope.cn/models/damo/cv_convnextTiny_ocr-recognition-licenseplate_damo/summary的训练部分时发现无法传参_cfg_modify_fn,php定义的_cfg_modify_fn拿到的cfg和返回的cfg均不符合python中定义的cfg类型

参照Dockerfile,使用的是Alpine镜像容器,php -m列表没有显示成功

image

执行php -m查看未安装成功
docker-php-source extract && \ phpize && \ ./configure --with-python-config=/usr/bin/python3-config && \ make && make install && \ docker-php-ext-enable phpy && \ docker-php-source delete
`Configuring for:
PHP Api Version: 20220829
Zend Module Api No: 20220829
Zend Extension Api No: 420220829
checking for grep that handles long lines and -e... /bin/grep
checking for egrep... /bin/grep -E
checking for a sed that does not truncate output... /bin/sed
checking for pkg-config... /usr/bin/pkg-config
checking pkg-config is at least version 0.9.0... yes
checking for cc... cc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether the compiler supports GNU C... yes
checking whether cc accepts -g... yes
checking for cc option to enable C11 features... none needed
checking how to run the C preprocessor... cc -E
checking for icc... no
checking for suncc... no
checking for system library directory... lib
checking if compiler supports -Wl,-rpath,... yes
checking build system type... x86_64-pc-linux-musl
checking host system type... x86_64-pc-linux-musl
checking target system type... x86_64-pc-linux-musl
checking for PHP prefix... /usr/local
checking for PHP includes... -I/usr/local/include/php -I/usr/local/include/php/main -I/usr/local/include/php/TSRM -I/usr/local/include/php/Zend -I/usr/local/include/php/ext -I/usr/local/include/php/ext/date/lib
checking for PHP extension directory... /usr/local/lib/php/extensions/no-debug-non-zts-20220829
checking for PHP installed headers prefix... /usr/local/include/php
checking if debug is enabled... no
checking if zts is enabled... no
checking for gawk... no
checking for nawk... no
checking for awk... awk
checking if awk is broken... no
checking dir of python... no
checking version of python... no
checking path of python_config... /usr/bin/python3-config
checking whether to enable phpy support... yes, shared
checking for g++... g++
checking whether the compiler supports GNU C++... yes
checking whether g++ accepts -g... yes
checking for g++ option to enable C++11 features... none needed
checking how to run the C++ preprocessor... g++ -E
checking for a sed that does not truncate output... /bin/sed
checking for ld used by cc... /usr/x86_64-alpine-linux-musl/bin/ld
checking if the linker (/usr/x86_64-alpine-linux-musl/bin/ld) is GNU ld... yes
checking for /usr/x86_64-alpine-linux-musl/bin/ld option to reload object files... -r
checking for BSD-compatible nm... /usr/bin/nm -B
checking whether ln -s works... yes
checking how to recognize dependent libraries... pass_all
./configure: line 6372: /usr/bin/file: not found
checking for stdio.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for strings.h... yes
checking for sys/stat.h... yes
checking for sys/types.h... yes
checking for unistd.h... yes
checking for dlfcn.h... yes
checking how to run the C++ preprocessor... g++ -E
checking the maximum length of command line arguments... 98304
checking command to parse /usr/bin/nm -B output from cc object... ok
checking for objdir... .libs
checking for ar... ar
checking for ranlib... ranlib
checking for strip... strip
checking if cc supports -fno-rtti -fno-exceptions... no
checking for cc option to produce PIC... -fPIC
checking if cc PIC flag -fPIC works... yes
checking if cc static flag -static works... no
checking if cc supports -c -o file.o... yes
checking whether the cc linker (/usr/x86_64-alpine-linux-musl/bin/ld) supports shared libraries... yes
checking whether -lc should be explicitly linked in... no
checking dynamic linker characteristics... GNU/Linux ld.so
checking how to hardcode library paths into programs... immediate
checking whether stripping libraries is possible... yes
checking if libtool supports shared libraries... yes
checking whether to build shared libraries... yes
checking whether to build static libraries... no

creating libtool
appending configuration tag "CXX" to libtool
checking for ld used by g++... /usr/x86_64-alpine-linux-musl/bin/ld
checking if the linker (/usr/x86_64-alpine-linux-musl/bin/ld) is GNU ld... yes
checking whether the g++ linker (/usr/x86_64-alpine-linux-musl/bin/ld) supports shared libraries... yes
checking for g++ option to produce PIC... -fPIC
checking if g++ PIC flag -fPIC works... yes
checking if g++ static flag -static works... no
checking if g++ supports -c -o file.o... yes
checking whether the g++ linker (/usr/x86_64-alpine-linux-musl/bin/ld) supports shared libraries... yes
checking dynamic linker characteristics... GNU/Linux ld.so
(cached) (cached) checking how to hardcode library paths into programs... immediate
configure: patching config.h.in
configure: creating ./config.status
config.status: creating config.h
config.status: config.h is unchanged

Build complete.
Don't forget to run 'make test'.

Installing shared extensions: /usr/local/lib/php/extensions/no-debug-non-zts-20220829/
fetch https://mirrors.ustc.edu.cn/alpine/v3.19/main/x86_64/APKINDEX.tar.gz
fetch https://mirrors.ustc.edu.cn/alpine/v3.19/community/x86_64/APKINDEX.tar.gz
(1/1) Installing .docker-php-ext-enable-deps (20240528.085413)
OK: 409 MiB in 119 packages
(1/1) Purging .docker-php-ext-enable-deps (20240528.085413)
OK: 409 MiB in 118 packages`

不知道是不是Linux的Alpine容器环境下,会出现这个情况,因为Alpine容器的安装方式都不一样,采用的是apk add,您这个是apt install

`/usr/bin # php -m
[PHP Modules]
Core
ctype
curl
date
dom
event
exif
fileinfo
filter
gd
hash
iconv
json
libxml
mbstring
mysqlnd
openssl
pcntl
pcre
PDO
pdo_mysql
pdo_sqlite
Phar
posix
random
readline
Reflection
session
SimpleXML
sockets
sodium
SPL
sqlite3
standard
sysvsem
tokenizer
xml
xmlreader
xmlwriter
Zend OPcache
zip
zlib

[Zend Modules]
Zend OPcache`

编译出错,PHP需要什么版本的

In file included from /root/src/phpy/src/bridge/core.cc:18: /root/src/phpy/include/phpy.h:200:15: error: ‘zend_result’ does not name a type; did you mean ‘zend_resource’? 200 | static inline zend_result call_fn( | ^~~~~~~~~~~ | zend_resource /root/src/phpy/src/bridge/core.cc: In function ‘PyObject* array2py(zval*)’: /root/src/phpy/src/bridge/core.cc:235:9: error: ‘zend_array_is_list’ was not declared in this scope; did you mean ‘zend_ast_is_list’? 235 | if (zend_array_is_list(ht)) { | ^~~~~~~~~~~~~~~~~~ | zend_ast_is_list /root/src/phpy/src/bridge/core.cc:234:17: warning: control reaches end of non-void function [-Wreturn-type] 234 | zend_array *ht = Z_ARRVAL_P(zv); | ^~ make: *** [Makefile:196: src/bridge/core.lo] Error 1
PHP需要什么版本的,还有PYTHON make怎么指定,我是手工个修改里面的路径的

python类继承,PHP怎么实现

例如Python有个类

class Animal(object):
   def __init__(self, name, age):
       self.name = name
       self.age = age

   def call(self):
       print(self.name, '会叫')

php怎么实现Cat类继承Animal呢?

class Cat(Animal):
   def __init__(self,name,age,sex):
       super(Cat, self).__init__(name,age)  # 不要忘记从Animal类引入属性
       self.sex=sex

dockerfile参考,已跑通模型

  • 环境
    Ubuntu 22.04.3 LTS
    NVIDIA-SMI 520.61.05 Driver Version: 520.61.05 CUDA Version: 11.8
    3080x2

  • 效果
    image

  • Dockefile

FROM nvidia/cuda:11.8.0-devel-ubuntu22.04

RUN apt-get update && \
    apt-get install -y --no-install-recommends build-essential git wget software-properties-common && \
    add-apt-repository ppa:ondrej/php && apt-get update && \
	DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends php-cli php-dev && \
	rm -rf /var/lib/apt/lists/*

RUN wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O ~/miniconda.sh && \
    chmod +x ~/miniconda.sh && \
    mkdir -p /opt/conda && \
    ~/miniconda.sh -b -u -p /opt/conda && \
    rm ~/miniconda.sh && \
    /opt/conda/bin/conda init bash

ENV PATH="/opt/conda/bin:$PATH"

RUN conda install pytorch torchvision torchaudio pytorch-cuda=11.8 -c pytorch -c nvidia && \
	conda install -c huggingface transformers

RUN git clone https://github.com/swoole/phpy.git /app/phpy

WORKDIR /app/phpy

RUN phpize && \
    ./configure --with-python-dir=/opt/conda && \
    make install && \
    echo "extension=phpy.so" > /etc/php/8.2/cli/conf.d/20_phpy.ini

RUN php -m | grep -i phpy

CMD [ "bash" ]
  • 使用
docker run --rm -it --gpus all --name phpy phpy bash
# docker run -d -it --gpus all -v /data/app:/app -v /data/conda:/opt/conda -v /data/cache:/root/.cache --name phpy phpy
cd examples
php pipeline.php
  • 安装docker和cuda
sudo apt-get update
sudo apt-get install \
 ca-certificates \
 curl \
 gnupg \
 lsb-release -y
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io -y

distribution=$(. /etc/os-release;echo $ID$VERSION_ID) \
&& curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - \
&& curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list
sudo apt-get update
sudo apt-get install -y nvidia-docker2

wget https://developer.download.nvidia.com/compute/cuda/11.8.0/local_installers/cuda_11.8.0_520.61.05_linux.run 
sudo sh ./cuda_11.8.0_520.61.05_linux.run

模块加载报错

/usr/local/php83/bin/php -m
PHP Warning: PHP Startup: Unable to load dynamic library 'phpy.so' (tried: /usr/local/php83/lib/php/extensions/no-debug-non-zts-20230831/phpy.so (/usr/local/php83/lib/php/extensions/no-debug-non-zts-20230831/phpy.so: undefined symbol: _ZNSt8ios_base4InitD1Ev), /usr/local/php83/lib/php/extensions/no-debug-non-zts-20230831/phpy.so.so (/usr/local/php83/lib/php/extensions/no-debug-non-zts-20230831/phpy.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
PHP需要怎么编译,是不是不能启用php-fpm?

这种情况是什么原因?

image

通常导入一些第三方包的时候会提示 Segmentation fault

内置包没有问题

1.0.3和1.0.4都这种问题

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.