@platformatic/php-node

repository·main·Indexed 19 days ago

https://github.com/platformatic/php-node

A Rust-based library that embeds the PHP runtime directly into Node.js applications, allowing PHP to run within the same process to eliminate network overhead. It features a custom PHP SAPI, N-API bindings, and built-in request rewriting functionality similar to mod_rewrite. The package provides binaries for various architectures including macOS (ARM64), Linux (x64, ARM64, ARMv7 with glibc or musl), and Windows (x64, ARM64, ia32).

Tokens
10.1K
Snippets
28
Records
56
Agent score
62%

What's inside @platformatic/php-node

  1. What is php-node?

    main

    php-node is a Rust library designed to embed the PHP runtime directly into Node.js applications. This allows PHP scripts to handle HTTP requests within the same process as Node.js, which provides several advantages:

    • Zero network overhead: Since PHP runs in the same process as Node.js, there is no network latency between the two.
    • Seamless interoperability: Enables direct interaction between PHP and Node.js.
    • Request rewriting: Includes built-in functionality similar to Apache's mod_rewrite.
    • Thread-safety: Supports concurrent handling of multiple PHP requests.
    • NAPI bindings: Exposes the underlying Rust functionality to Node.js via N-API.
    • Custom SAPI: Uses a custom PHP Server API (SAPI) implementation for high performance.
    • Reusable environments: Reuses compiled PHP code across different requests to minimize startup costs.
  2. Overview of php-stackable-linux-x64-musl

    main
    The php-stackable-linux-x64-musl package provides the x86_64-unknown-linux-musl binary for php-stackable. This specific build is intended for Linux environments using the musl C library (commonly found in Alpine Linux or other lightweight distributions) on x86_64 architecture.
  3. Identify the `@platformatic/php-linux-arm-gnueabihf` package

    main
    The @platformatic/php-linux-arm-gnueabihf package provides the armv7-unknown-linux-gnueabihf binary for @platformatic/php. Use this specific package when your target environment is an ARMv7 Linux system using the gnueabihf (GNU Embedded Application Binary Interface with Hard Float) ABI.
  4. Understand the core modules of php-node

    main

    The php-node project is structured into several core modules that manage the PHP lifecycle and integration:

    • lib.rs: The library entry point and public API surface. It exports the Embed runtime wrapper, Handler trait for async HTTP handling, and Request/Response types.
    • embed.rs: Contains the core PHP embedding logic and the Embed struct, which represents a PHP runtime instance.
    • sapi.rs: Implements a custom PHP SAPI (Server API) to manage the PHP lifecycle and handle C FFI callbacks (like writing output or reading POST data).
    • request_context.rs: Manages thread-local request state, allowing C-style SAPI callbacks to access request and response data safely.
    • scopes.rs: Uses RAII (Resource Acquisition Is Initialization) patterns via RequestScope and FileHandleScope to ensure PHP resources are cleaned up even if a script crashes or exits.
    • napi.rs: Provides Node.js N-API bindings (when enabled) to expose the PHP runtime to JavaScript via the PhpRuntime class.
    • exception.rs: Defines error types such as EmbedStartError (startup issues) and EmbedRequestError (runtime issues like PHP exceptions or bailouts).
  5. Identify the correct @platformatic/php binary for Linux ARM (musl)

    main
    The @platformatic/php-linux-arm-musleabihf package provides the armv7-unknown-linux-musleabihf binary for @platformatic/php. Use this specific package if your target environment is a Linux system running on ARM architecture with the musl C library (common in Alpine Linux or other lightweight distributions).
  6. Manage Memory and C Strings in PHP Embedding

    main

    When interacting with PHP's C APIs, you must use PHP's internal allocator to ensure compatibility and prevent memory corruption.

    C String Rules

    • Allocation: Use estrdup(str) to allocate a string using PHP's allocator.
    • Deallocation: Every string allocated via estrdup must be manually freed using efree(ptr) or the safe wrapper maybe_efree(ptr).
    • Ownership: Never mix libc malloc/free with PHP's emalloc/efree.
    • Lifecycle: Strings should typically be freed in the sapi_module_deactivate callback.
    • Constraint: Do not use strings from any source other than estrdup when passing data to PHP types or functions.

    Request Context Lifecycle

    RequestContext is stored in the PHP SAPI server_context global. It is managed using a Box<T> pattern:

    1. Allocation: Convert a Box to a raw pointer using Box::into_raw and store it in globals.server_context.
    2. Access: Reconstruct a mutable reference using unsafe { &mut *(ptr as *mut RequestContext) }.
    3. Deallocation: Reclaim ownership using Box::from_raw(ptr) so the Box can be dropped and memory freed. Failure to reclaim the Box before the request ends will cause a memory leak.
    // Allocation
    let context = Box::new(RequestContext { ... });
    let raw_ptr = Box::into_raw(context) as *mut c_void;
    globals.server_context = raw_ptr;
    
    // Access
    let ctx = unsafe { &mut *(ptr as *mut RequestContext) };
    
    // Deallocation
    let boxed = unsafe { Box::from_raw(ptr as *mut RequestContext) };
  7. How PHP embedding works via SAPI and Zend API

    main

    This project interacts with PHP at a level deeper than the standard Server API (SAPI) to improve performance.

    SAPI (Server API)

    SAPI is the interface between PHP and a web server. While it is the recommended way to embed PHP, using it directly causes high startup costs because it spins up a fresh PHP instance for every request.

    Zend API Scopes

    To achieve better performance, the project utilizes the underlying Zend API scopes:

    1. php_tsrm_startup: Provides thread safety for running multiple PHP environments in parallel.
    2. zend_signal_startup: Defines global signal handling.
    3. sapi_startup: Initializes the SAPI, loads INI settings, extensions, and allocates space for superglobals.
    4. php_embed_module.startup: A configurable part of SAPI that allows treating the constructed server as a module. It handles populating $_POST, $_GET, $_COOKIE, $_ENV, $_SERVER, php://input, and managing response headers/body.
    5. php_request_startup: The scope where the actual request occurs. It allocates request-specific superglobals and environment settings.

    Performance Strategy: Instead of using SAPI (which tears down everything after one request), the project aims to reuse the SAPI/environment setup and only re-configure the php_embed_module for each individual request. This allows sharing code compilation and environment state across requests.

  8. How php-node architecture works

    main

    The architecture of php-node is organized into layers that bridge the gap between Node.js and the PHP Zend Engine. The flow of a request follows this hierarchy:

    1. NAPI Layer (napi.rs): The JavaScript-facing interface. It contains the PhpRuntime (the PHP instance visible to JS) and PhpRequestTask (the async handler for requests).
    2. Core Embed Layer (embed.rs): The main request handler. It manages request rewriting logic and implements the HTTP Handler trait.
    3. SAPI Implementation (sapi.rs): A custom PHP SAPI module that manages the SAPI lifecycle, handles INI configurations, and provides callbacks for I/O operations.
    4. Request Context (request_context.rs): Manages thread-local request state, accumulates response data, and provides access to request/response information.
    5. PHP Runtime (via ext-php-rs): The bottom layer containing the Zend Engine, which performs actual script execution and exception handling.
  9. Understand PHP request/response streams and superglobals

    main

    When embedding PHP, data is transmitted via specific streams and superglobal variables:

    Streams

    • php://input: Represents the body of an incoming request.
    • php://output: Used for writing the response body.

    Superglobals

    Since PHP uses streams for bodies, headers and other metadata are passed via superglobals. The main ones are:

    • $_SERVER: Server and request information.
    • $_GET: Query string parameters.
    • $_POST: Form data.
    • $_FILES: File uploads.
    • $_COOKIE: Cookies.
    • $_SESSION: Session data.
    • $_REQUEST: A combination of $_GET, $_POST, and $_COOKIE.
    • $_ENV: Environment variables.
  10. Understand the PHP Embedding and SAPI Architecture

    main

    The project embeds PHP using the ext-php-rs library, which provides safe Rust bindings to PHP/Zend C APIs. It implements a custom SAPI (Server API) named php_lang_handler.

    Unlike a standard CLI SAPI, this implementation is modified for request/response handling and allows for request scope reuse without a full teardown, improving performance.

    Key Concepts

    • SAPI: The interface between PHP and the web server. It handles I/O and manages the lifecycle of a request.
    • Zend API: The lower-level engine API. While the project uses SAPI for high-level management, it interacts with Zend for core execution.
    • Bailout: PHP's mechanism (using longjmp) for fatal errors. Because bailouts skip normal execution flow, the project uses specific unwinding patterns to prevent memory leaks and segfaults.