Unlock Python Power in PHP with phpy: New Features and Practical Examples

This article introduces phpy, an open‑source bridge that lets PHP import and execute Python packages, and walks through its latest features such as the with‑syntax, socket handle conversion, exception propagation, slicing, enum definition, annotated functions, and environment variable handling with concrete code samples.

Open Source Tech Hub
Open Source Tech Hub
Open Source Tech Hub
Unlock Python Power in PHP with phpy: New Features and Practical Examples

Overview

phpy is an open‑source project that brings the Python ecosystem into PHP, allowing PHP code to import and call any Python package.

with Syntax

The Python with statement can be expressed in phpy using PyWith .

Python Example

with open(file, 'wb') as fp:
    fp.write(some_bytes)

PHP Example

PyWith(function ($fp) use ($bytes) {
    $fp->write($bytes);
}, PyCore::open($tmpname, 'wb'));

Socket Handle Transfer

Convert a Python socket object to a PHP stream resource and vice‑versa.

Python → 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 Socket.fileno() to obtain the file descriptor.

Use fopen('php://fd/{$fileno}') to create a PHP stream.

Read/write/close with fwrite/fread/fclose. The descriptor is duplicated, so closing the PHP stream does not affect the original Python socket.

PHP → Python

$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)
");
}
$client = $socket->fromfd(PyCore::fileno($fp), $socket->AF_INET, $socket->SOCK_STREAM);
fclose($fp);

Use PyCore::fileno($fp) to get the PHP stream descriptor.

Use socket.fromfd() to create a Python socket from that descriptor. The descriptor is copied, so closing the Python socket does not affect the PHP stream.

Throwing Exceptions from PHP to Python

$message = $m->test_raise(function () use ($builtins) {
    PyCore::raise($builtins->ValueError, "test raise");
});

Python side catches the exception:

def test_raise(fn):
    try:
        fn()
    except ValueError as e:
        return str(e)

Slice Syntax

Use PyCore::slice to perform Python‑style slicing on strings or other sequences.

$s = new PyStr("Python Programming");
$this->assertEquals($s[PyCore::slice(0, 3)], "Pyt");
$this->assertEquals($s[PyCore::slice(7, 12)], "Progr");
$this->assertEquals($s[PyCore::slice(null, null, null)], "Python Programming");
$this->assertEquals($s[PyCore::slice(null, null, 2)], "Pto rgamn");
$this->assertEquals($s[PyCore::slice(null, null, -1)], "gnimmargorP nohtyP");
$this->assertEquals($s[PyCore::slice(-1, null)], "g");
$this->assertEquals($s[PyCore::slice(-3, -1)], "in");

Chinese strings also work:

$str = new PyStr("我是中国人");
$this->assertEquals($str[0], '我');
$this->assertEquals($str[2], '中');
$this->assertEquals($str[PyCore::slice(2, 4)], '中国');

Define Python Enum

Convert a PHP class containing only constants into a Python Enum with PyEnum.

class Medium {
    const Glass = 1.520;
    const Oil   = 1.515;
    const Water = 1.333;
    const Air   = 1.0003;
}
$medium = PyEnum(Medium::class);
PyCore::print($medium->Glass->value);

Define Python Function with Annotations

Wrap a PHP function with metadata for Python libraries using PyNamedFn.

#[PyImport('magicgui', 'magicgui')]
#[PyImport('Medium', 'enums.Medium')]
#[PyAnnotation('@magicgui(call_button="calculate", result_widget=True)')]
#[PyArguments('aoi=30', 'n1=Medium.Glass', 'n2=Medium.Water', 'degrees=True')]
function snells_law($aoi, $n1, $n2, $degrees) {
    $math = PyCore::import('math');
    $aoi = $degrees ? $math->radians($aoi) : $aoi;
    try {
        $result = $math->asin($n1->value * $math->sin($aoi) / $n2->value);
        return $degrees ? $math->degrees($result) : $result;
    } catch (PyError $e) {
        if (PyCore::isinstance($e, $builtins->ValueError)) {
            $result = $math->asin($n1->value * $math->sin($aoi) / $n2->value);
            return $degrees ? $math->degrees($result) : $result;
        } else {
            throw $e;
        }
    }
}
PyNamedFn('snells_law')->show(run: true);
Full source: https://github.com/swoole/phpy/blob/main/examples/magicgui/hello.php

Passing Environment Variables

Copy PHP environment variables into the embedded Python interpreter.

$os = PyCore::import('os');
foreach ($_ENV as $k => $v) {
    $os->environ[$k] = $v;
}
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

PythonenumSocketInteropfunctionphpy
Open Source Tech Hub
Written by

Open Source Tech Hub

Sharing cutting-edge internet technologies and practical AI resources.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.