# PHP and C++ Mixed Programming Guide ## 📋 Overview The AOT compiler allows you to use both `.php` and `.cpp/.cc` code in the same project, enabling mixed PHP and C++ programming. This mechanism lets you: - ✅ Write high-performance core algorithms in C++ - ✅ Write business logic and interfaces in PHP - ✅ Call seamlessly with zero performance loss --- ## 🎯 Core Mechanism ### Exposing C++ Functions to PHP A C++ function can be called directly from PHP code when it meets the following conditions: 1. **Parameter types**: Must all be `php::` types (such as `php::Int`, `php::Str`, `php::Float`, etc.) 2. **Return type**: Must be a `php::` type 3. **Function naming**: Must start with the `php_` prefix 4. **Stub file**: Must have a corresponding `.stub.php` file declaring the function signature --- ## 📦 Box Wrapper Mechanism ### Overview `php::Box` is a C++ class wrapper provided by the AOT compiler. It allows: - ✅ C++ objects to be automatically managed by the PHP GC (garbage collector) - ✅ No need to manually free memory - ✅ Storage in PHP arrays - ✅ Storage as object properties - ✅ Representation as a `resource` type at the PHP layer ### Basic Usage #### Step 1: Define a C++ class and inherit from php::Box ```cpp #include using namespace php; // Custom C++ class, inheriting from php::Box class VectorBox : public Box { public: std::vector vec; // Constructor VectorBox(size_t size, bool init) { vec.resize(size, init); } // Member method void checkOffset(Int offset) { if (offset >= vec.size()) { zend_throw_error(NULL, "index[%ld] is out of range()", offset); } } }; ``` #### Step 2: Create an object and return it to PHP ```cpp // Create a Box object and return it to PHP var php_vector_new(Int size, Bool init) { // new a VectorBox, wrap it as php::Var and return return {new VectorBox(size, init)}; } ``` **Key points**: - ✅ Use `new` to create the object - ✅ Use `{}` to wrap it as a `php::Var` return value - ✅ No need to manually `delete`; PHP GC will free it automatically #### Step 3: Receive and use it in PHP **PHP code** (`main.php`): ```php vector = $vector; // Pass to other C++ functions vector_set($vector, 5, false); $value = vector_get($vector, 5); } ``` #### Step 4: Convert back to an object pointer in C++ ```cpp // Receive a Box parameter of php::Var type Bool php_vector_get(var box, Int offset) { // Convert php::Var to a C++ object pointer auto vecbox = box.toBox(); // Now you can access the members of the C++ object vecbox->checkOffset(offset); return vecbox->vec.at(offset); } void php_vector_set(var box, Int offset, Bool value) { // Convert to an object pointer auto vecbox = box.toBox(); // Modify the object state vecbox->checkOffset(offset); vecbox->vec.at(offset) = value; } ``` **Key points**: - ✅ Use `box.toBox()` to convert to a concrete type - ✅ The template argument must be the actual class name - ✅ After conversion you can directly access member variables and methods --- ### Complete Example: VectorBox #### C++ Implementation (`vector.cc`) ```cpp #include #include using namespace php; // 1. Define the Box class class VectorBox : public Box { public: std::vector vec; VectorBox(size_t size, bool init) { vec.resize(size, init); } void checkOffset(Int offset) { if (offset >= vec.size()) { zend_throw_error(NULL, "index[%ld] is out of range()", offset); } } }; // 2. Function to create the object var php_vector_new(Int size, Bool init) { return {new VectorBox(size, init)}; } // 3. Function to get an element Bool php_vector_get(var box, Int offset) { auto vecbox = box.toBox(); vecbox->checkOffset(offset); return vecbox->vec.at(offset); } // 4. Function to set an element void php_vector_set(var box, Int offset, Bool value) { auto vecbox = box.toBox(); vecbox->checkOffset(offset); vecbox->vec.at(offset) = value; } // 5. Function to get the size Int php_vector_size(var box) { auto vecbox = box.toBox(); return vecbox->vec.size(); } ``` #### PHP Stub File (`vector.stub.php`) ```php vector = vector_new(50, true); echo "Vector size in container: " . vector_size($container->vector) . "\n"; } ``` --- ### Advantages of the Box Wrapper #### 1. Automatic Memory Management ```cpp // ❌ Without Box: manual memory management required class MyObject { // ... }; MyObject* obj = new MyObject(); // ... use delete obj; // Must delete manually, easy to forget // ✅ With Box: PHP GC manages it automatically class MyBox : public php::Box { // ... }; php::Var result = {new MyBox()}; // PHP GC will free it at the appropriate time ``` #### 2. Type Safety ```cpp // Compile-time type checking auto box = box_var.toBox(); // Type is explicit // If the type does not match, an error is raised at compile time or runtime ``` #### 3. Ease of Use ```cpp // Simple conversion syntax auto ptr = box.toBox(); // Directly access members ptr->method(); ptr->property = value; ``` --- ### Notes #### ⚠️ 1. Must inherit from php::Box ```cpp // ✅ Correct class MyClass : public php::Box { // ... }; // ❌ Wrong: will not be managed by the PHP GC class MyClass { // ... requires manual freeing }; ``` #### ⚠️ 2. Use new to create objects ```cpp // ✅ Correct: use new return {new VectorBox(size, init)}; // ❌ Wrong: stack objects will not be managed by the GC VectorBox box(size, init); return {&box}; // Dangling pointer! ``` #### ⚠️ 3. Correct toBox conversion ```cpp // ✅ Correct: specify the correct type auto ptr = box.toBox(); // ❌ Wrong: type mismatch auto ptr = box.toBox(); // Runtime error ``` #### ⚠️ 4. Resource validity check ```cpp // Recommended: check whether the resource is valid before use Bool php_vector_get(var box, Int offset) { if (box.isNull()) { zend_throw_error(NULL, "Invalid box resource"); return false; } auto vecbox = box.toBox(); // ... } ``` --- ### Real-world Application Scenarios #### Scenario 1: Data Structure Wrapping ```cpp // Wrap a C++ STL container class HashMapBox : public php::Box { public: std::unordered_map map; }; var php_hashmap_new() { return {new HashMapBox()}; } void php_hashmap_set(var box, Str key, Int value) { auto hashmap = box.toBox(); hashmap->map[key.to_string()] = value; } ``` #### Scenario 2: Image Processing ```cpp // Wrap an image resource class ImageBox : public php::Box { public: cv::Mat image; ImageBox(const std::string& path) { image = cv::imread(path); } }; var php_image_load(Str path) { return {new ImageBox(path.to_string())}; } var php_image_resize(var box, Int width, Int height) { auto img = box.toBox(); cv::resize(img->image, img->image, cv::Size(width, height)); return box; // Return the same object } ``` #### Scenario 3: Database Connection ```cpp // Wrap a database connection class DatabaseBox : public php::Box { public: MYSQL* conn; DatabaseBox(const std::string& host, const std::string& user, const std::string& pass, const std::string& db) { conn = mysql_init(NULL); mysql_real_connect(conn, host.c_str(), user.c_str(), pass.c_str(), db.c_str(), 0, NULL, 0); } ~DatabaseBox() { mysql_close(conn); } }; var php_db_connect(Str host, Str user, Str pass, Str db) { return {new DatabaseBox(host.to_string(), user.to_string(), pass.to_string(), db.to_string())}; } ``` --- ## 📝 Basic Syntax ### Step 1: Write the C++ function implementation **Example file**: `examples/prime/src/prime.cc` ```cpp #include "phpx.h" #include "phpx_helper.h" using namespace php; /** * Determine whether a number is prime * * @param n the number to check * @return bool whether it is prime */ bool php_is_prime(php::Int n) { if (n < 2) { return false; } for (php::Int i = 2; i * i <= n; i++) { if (n % i == 0) { return false; } } return true; } /** * Get all prime numbers within the given range * * @param start start number * @param end end number * @return array array of primes */ php::Array php_get_primes(php::Int start, php::Int end) { php::Array primes; for (php::Int i = start; i <= end; i++) { if (php_is_prime(i)) { primes.append(i); } } return primes; } /** * Compute the product of two large numbers * * @param a first number * @param b second number * @return int product result */ php::Int php_multiply_big_numbers(php::Int a, php::Int b) { return a * b; } ``` --- ### Step 2: Create the .stub.php stub file **Example file**: `examples/prime/src/prime.stub.php` ```php .stub.php`. This published stub automatically carries `@import-library`; once loaded by another project, all of its functions and class methods are imported according to the external library ABI. The library name is derived from the file name; for example, `prime2.stub.php` corresponds to the `prime2` library. Classes in an external stub generate class registrations, properties, and constant entities in the consuming project, but do not generate `php_*` method bodies; the method bodies are provided by the dynamic library. Property hooks are handled the same way as methods: the published stub keeps the `get`/`set` declarations and removes the implementations; the consuming project generates the property entities, and the hook getter/setter `php_*` implementations are imported from the dynamic library. Declarations internal to a library can be excluded from the public ABI using the compile-time Attribute `#[NoExport]`: ```php #[\NoExport] function internal_helper(): void {} #[\NoExport] class InternalService {} ``` The declarations still participate in the current library's compilation, but do not enter `.stub.php`, and the corresponding `php_*` symbols are not given the library export modifier. A class annotation cascades to all of its methods; individual methods can also be marked independently. `NoExport` lives in the root namespace: in the global namespace you write `#[NoExport]`, in other namespaces you must write `#[\NoExport]`, and this compile-time Attribute does not enter runtime metadata. Both `NoExport` and `ExtensionProvider` follow PHP class name resolution rules, supporting fully qualified names, `use`, and `use ... as ...` aliases. The compiler only consumes the Attribute when the resolution strictly points to the built-in Attribute in the root namespace. `php__func_decl.h` and `php__data_decl.h` are both internal generated files of the TypePHP build process, not public development headers of the library. `func_decl.h` is also force-included during `-m lib` builds to add platform export markers to the current target's `php_*` C++ ABI functions; `data_decl.h` only declares global variables, constant objects, and literal/runtime mapping accessors within the target. These project data declarations live in the `typephp_` C++ namespace; the underlying literal/cache tables are kept in `extension-.cc`, and other translation units access them only through accessors such as `get_str()`, `get_class()`, and `get_func()`, without depending directly on the storage. When publishing a TypePHP library, provide: - The `.stub.php` automatically generated by `-m lib`; - The `.dll` and import library `.lib` on Windows; - The `.so` on Linux and other platforms. If the library additionally exports a custom C++ ABI or C ABI, the library author needs to write and publish the corresponding `.h` header file together with the library. ✅ **Correct**: ```php php::Object php_resize_image(php::Object img, php::Int width, php::Int height) { // Use OpenCV for image scaling cv::Mat mat = ...; // Extract from a PHP object cv::Mat resized; cv::resize(mat, resized, cv::Size(width, height)); // Return the new image object return create_image_object(resized); } php::Array php_detect_faces(php::Object img) { // Use Haar cascades to detect faces // Return the array of detected face coordinates php::Array faces; // ... detection logic return faces; } ``` **PHP call** (`app.php`): ```php function process_images() { $img = image_create_from_file('photo.jpg'); // Call C++ functions $resized = resize_image($img, 800, 600); $faces = detect_faces($resized); echo "Detected " . count($faces) . " faces\n"; } ``` ### Case 2: Encryption and Decryption **C++ implementation** (`crypto.cc`): ```cpp #include "phpx.h" #include php::Str php_aes_encrypt(php::Str data, php::Str key) { // Use OpenSSL for AES encryption // High-performance hardware acceleration php::Str encrypted; // ... encryption logic return encrypted; } php::Str php_aes_decrypt(php::Str encrypted, php::Str key) { // Decrypt data php::Str decrypted; // ... decryption logic return decrypted; } ``` **PHP call** (`security.php`): ```php function secure_communication() { $data = "sensitive information"; $key = "secret key"; // Call the C++ encryption function $encrypted = aes_encrypt($data, $key); // Transmit the encrypted data... // Call the C++ decryption function $decrypted = aes_decrypt($encrypted, $key); echo "Decryption result: {$decrypted}\n"; } ``` ### Case 3: Database Operations **C++ implementation** (`database.cc`): ```cpp #include "phpx.h" #include php::Array php_query_users(php::Int min_age, php::Int max_age) { // Connect directly to the MySQL database // High-performance batch query php::Array users; MYSQL* conn = mysql_init(NULL); mysql_real_connect(conn, "localhost", "user", "pass", "db", 0, NULL, 0); std::string query = "SELECT * FROM users WHERE age BETWEEN "; query += std::to_string(min_age) + " AND " + std::to_string(max_age); mysql_query(conn, query.c_str()); MYSQL_RES* result = mysql_store_result(conn); while (MYSQL_ROW row = mysql_fetch_row(result)) { php::Array user; user.set("id", row[0]); user.set("name", row[1]); user.set("age", row[2]); users.append(user); } mysql_free_result(result); mysql_close(conn); return users; } ``` **PHP call** (`user_service.php`): ```php function get_adult_users() { // Call the C++ database query $users = query_users(18, 65); // PHP handles the business logic foreach ($users as $user) { if ($user['age'] >= 30) { echo "Senior user: {$user['name']}\n"; } } } ``` --- ## 🔍 Debugging Tips ### 1. Inspect the generated code ```bash # Keep the intermediate files php bin/tpc.php project --dry --build-dir /tmp/typephp-build # Inspect the generated C++ code find /tmp/typephp-build -name '*.cc' -o -name '*.cpp' ``` ### 2. Type Checking ```cpp // Add type checking in C++ code php::Int php_safe_add(php::Int a, php::Int b) { // Check for overflow if (a > 0 && b > PHP_INT_MAX - a) { throw new OverflowException("Addition overflow"); } return a + b; } ``` ### 3. Performance Profiling ```bash # Add debug information at build time php bin/tpc.php project -o app --debug # Use perf for performance analysis perf record ./app perf report ``` --- ## ⚡ Performance Comparison ### Benchmarks | Operation | PHP implementation | C++ implementation | Speedup | |------|---------|---------|---------| | Prime check (1 million) | 5000ms | 50ms | **100x** | | Array sort (100k elements) | 800ms | 8ms | **100x** | | String concatenation (10k times) | 200ms | 2ms | **100x** | | Math computation (factorial 10000) | 1500ms | 5ms | **300x** | | Image scaling (100 images) | 3000ms | 300ms | **10x** | --- ## ❓ FAQ ### Q: Why do we need a .stub.php file? A: A `.stub.php` file serves three purposes: 1. **IDE support**: provides code hints and autocompletion 2. **Type checking**: the AOT compiler performs type validation at compile time 3. **Documentation**: serves as the PHP interface documentation for C++ functions ### Q: Can I call PHP functions from C++? A: Yes, but you need to go through the API provided by the PHPX framework: ```cpp php::Var result = php::call("php_function_name", args); ``` ### Q: How do I handle exceptions? A: Wrap them in try-catch in C++ and convert to PHP exceptions: ```cpp php::Int php_divide(php::Int a, php::Int b) { if (b == 0) { throw new InvalidArgumentException("Division by zero"); } return a / b; } ``` ### Q: Are C++ classes supported? A: Currently only free functions are supported. If you need object orientation, you can use the factory pattern: ```cpp php::Object php_create_calculator() { // Return a PHP object that wraps the C++ object return create_object("Calculator", internal_ptr); } php::Int php_calculator_add(php::Object calc, php::Int a, php::Int b) { Calculator* c = get_internal_pointer(calc); return c->add(a, b); } ``` --- ## 📚 Related Resources - **Example project**: `examples/prime/` - **PHPX framework documentation**: [link] - **C++ type system**: see [NATIVE_TYPES.md](NATIVE_TYPES.md) - **AOT compiler architecture**: see [Backend-neutral IR](BACKEND_NEUTRAL_IR.md) and [Core refactoring plan](REFACTORING_PLAN.md) --- **Last updated**: March 18, 2024 **Applicable version**: PHP AOT Compiler v1.x