diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index c3103eb..0dc79f4 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -15,7 +15,7 @@ jobs:
steps:
- name: Checkout code
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Set up PHP ${{ matrix.php-version }}
uses: shivammathur/setup-php@v2
@@ -34,9 +34,13 @@ jobs:
./configure
make
+ - name: Create php.ini
+ run: |
+ echo "extension=$(pwd)/modules/rayaop.so" > php.ini
+
- name: Run tests
id: run_tests
- run: make test
+ run: PHP_INI_SCAN_DIR=$(pwd) make test
continue-on-error: true
- name: Run demo with debug logging
@@ -78,7 +82,7 @@ jobs:
- name: Upload Valgrind log file
if: (steps.run_tests.outcome == 'failure' || steps.run_demo.outcome == 'failure') && always()
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v4
with:
name: valgrind-log
path: valgrind-out.txt
@@ -86,7 +90,7 @@ jobs:
- name: Upload test logs
if: failure()
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v4
with:
name: test-logs
path: |
diff --git a/.gitignore b/.gitignore
index c465303..4a3eb36 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,4 +30,5 @@
# Ignore directories and its content
/Makefile.fragments
/Makefile.objects
-/cmake-build-debug
\ No newline at end of file
+/cmake-build-debug
+/modules/rayaop.so
diff --git a/README.md b/README.md
index 56a71b1..ba9356a 100644
--- a/README.md
+++ b/README.md
@@ -4,165 +4,137 @@
-A PHP extension that provides Aspect-Oriented Programming (AOP) functionality for method interception, designed to complement Ray.Aop.
+Low-level PHP extension that provides core method interception functionality for [Ray.Aop](https://github.com/ray-di/Ray.Aop). While this extension can be used standalone, it is designed to be a foundation for Ray.Aop's more sophisticated AOP features.
## Features
-- Efficient method interception for specific classes
-- Apply custom logic before and after method execution
-- Works with `final` classes and methods
+- Efficient low-level method interception
+- Support for intercepting final classes and methods
+- Full parameter and return value modification support
+- Works seamlessly with the `new` keyword
+- Thread-safe operation support
## Requirements
- PHP 8.1 or higher
+- Linux, macOS, or Windows with appropriate build tools
+- Thread-safe PHP build recommended for multi-threaded environments
## Installation
1. Clone the repository:
-
-```
+```bash
git clone https://github.com/ray-di/ext-rayaop.git
cd ext-rayaop
```
2. Build and install the extension:
-
-```
+```bash
phpize
./configure
-make --enable-rayaop-quiet // Suppress E_NOTICE to indicate experimental.
+make
make install
```
3. Add the following line to your php.ini file:
-
+```ini
+extension=rayaop.so # For Unix/Linux
+extension=rayaop.dll # For Windows
```
-extension=rayaop.so
+
+4. Verify installation:
+```bash
+php -m | grep rayaop
```
-## About this Extension
+## Design Decisions
-This PECL extension is designed to enhance Ray.Aop by providing method interception capabilities at a lower level. While it can be used independently, it's primarily intended to be used in conjunction with Ray.Aop for optimal functionality.
+This extension provides minimal, high-performance method interception capabilities:
-Key points:
-- The extension intentionally binds only one interceptor per method for simplicity and performance.
-- Multiple interceptor chaining should be implemented in PHP code, either using Ray.Aop or custom implementation.
-- The main advantages are the ability to intercept `final` classes/methods and unrestricted use of the `new` keyword.
+- One interceptor per method: The extension supports a single active interceptor per method, with the last registered interceptor taking precedence
+- Final class support: Can intercept final classes and methods, unlike pure PHP implementations
+- Raw interception: No built-in matching or conditions (use Ray.Aop for these features)
+- Thread-safe: Safe to use in multi-threaded environments like PHP-FPM
-## Usage
+## Relationship with Ray.Aop
-### Defining an Interceptor
+This extension provides low-level method interception, while [Ray.Aop](https://github.com/ray-di/Ray.Aop) offers high-level AOP features:
-Create a class that implements the `Ray\Aop\MethodInterceptorInterface`:
+Ray.Aop provides:
+- Conditional interception using Matchers
+- Multiple interceptors per method
+- Attribute/Annotation based interception
+- Sophisticated AOP features
-```php
-namespace Ray\Aop {
- interface MethodInterceptorInterface
- {
- public function intercept(object $object, string $method, array $params): mixed;
- }
-}
+When both are used together:
+- Ray.Aop handles the high-level AOP logic
+- This extension provides the low-level interception mechanism
+- Ray.Aop automatically utilizes this extension when available for better performance
+
+## Basic Usage
-class MyInterceptor implements Ray\Aop\MethodInterceptorInterface
+### Simple Interceptor
+```php
+class LoggingInterceptor implements Ray\Aop\MethodInterceptorInterface
{
public function intercept(object $object, string $method, array $params): mixed
{
- echo "Before method execution\n";
- $result = call_user_func_array([$object, $method], $params);
- echo "After method execution\n";
+ echo "Before {$method}\n";
+ $result = $object->$method(...$params);
+ echo "After {$method}\n";
return $result;
}
}
-```
-
-### Registering an Interceptor
-Use the `method_intercept` function to register an interceptor for a specific class and method:
-
-```php
-$interceptor = new MyInterceptor();
-method_intercept('TestClass', 'testMethod', $interceptor);
+// Register the interceptor
+method_intercept(TestClass::class, 'testMethod', new LoggingInterceptor());
```
-### Complete Example
-
+### Method Interception Setup
```php
-class TestClass
-{
- public function testMethod($arg)
- {
- echo "TestClass::testMethod($arg) called\n";
- return "Result: $arg";
- }
-}
+// Initialize the interception system
+method_intercept_init();
-$interceptor = new MyInterceptor();
-method_intercept('TestClass', 'testMethod', $interceptor);
+// Enable method interception
+method_intercept_enable(true);
-$test = new TestClass();
-$result = $test->testMethod("test");
-echo "Final result: $result\n";
+// Register interceptors
+method_intercept(MyClass::class, 'myMethod', new MyInterceptor());
```
-Output:
-```
-Before method execution
-TestClass::testMethod(test) called
-After method execution
-Final result: Result: test
-```
-
-## Integration with Ray.Aop
-
-For more complex AOP scenarios, it's recommended to use this extension in combination with [Ray.Aop](https://github.com/ray-di/Ray.Aop). Ray.Aop provides a higher-level API for managing multiple interceptors and more advanced AOP features.
-
-## The Power of AOP
-
-Aspect-Oriented Programming (AOP) is a powerful paradigm that complements Object-Oriented Programming (OOP) in building more flexible and maintainable software systems. By using AOP:
-
-1. **Separation of Concerns**: You can cleanly separate cross-cutting concerns (like logging, security, or transaction management) from your core business logic.
-
-2. **Enhanced Modularity**: AOP allows you to modularize system-wide concerns that would otherwise be scattered across multiple classes.
-
-3. **Improved Code Reusability**: Aspects can be reused across different parts of your application, reducing code duplication.
-
-4. **Easier Maintenance**: By centralizing certain behaviors, AOP can make your codebase easier to maintain and evolve over time.
-
-5. **Non-invasive Changes**: You can add new behaviors to existing code without modifying the original classes, adhering to the Open/Closed Principle.
-
-6. **Dynamic Behavior Modification**: With this PECL extension, you can even apply aspects to final classes and methods, providing unprecedented flexibility in your system design.
+## Development
-By combining the strengths of OOP and AOP, developers can create more robust, flexible, and easier-to-maintain software architectures. This PECL extension, especially when used in conjunction with Ray.Aop, opens up new possibilities for structuring your PHP applications, allowing you to tackle complex problems with elegance and efficiency.
-
-## Build Script
-
-The `build.sh` script provides various operations for building and managing the extension:
+### Build Script
+```bash
+./build.sh clean # Clean build environment
+./build.sh prepare # Prepare build environment
+./build.sh build # Build extension
+./build.sh run # Run extension
+./build.sh all # Execute all steps
+```
-```sh
-./build.sh clean # Clean the build environment
-./build.sh prepare # Prepare the build environment
-./build.sh build # Build the extension
-./build.sh run # Run the extension
-./build.sh all # Execute all the above steps
+### Testing
+```bash
+make test
```
-Use `./build.sh all` for a complete build and installation process.
+For specific tests:
+```bash
+make test TESTS="-v tests/your_specific_test.phpt"
+```
-## Running Tests
+## License
-To run the tests for this extension, use the following command:
+[MIT License](LICENSE)
-```sh
-make test
-```
+## Author
-This command will execute the PHP extension's test suite, which is the standard method for testing PHP extensions.
+Akihito Koriyama
-If you need to run specific tests or want more verbose output, you can use:
+This extension was developed with the assistance of AI pair programming, which helped navigate the complexities of PHP extension development and PECL standards.
-```sh
-make test TESTS="-v tests/your_specific_test.phpt"
-```
+## Acknowledgments
-Replace `your_specific_test.phpt` with the actual test file you want to run.
+This project was created using [JetBrains CLion](https://www.jetbrains.com/clion/), which is available for free with an [Open Source License](https://www.jetbrains.com/community/opensource/).
+We'd like to express our gratitude to JetBrains for providing such a powerful and user-friendly development environment.
\ No newline at end of file
diff --git a/config.m4 b/config.m4
index 4244685..e4b69bd 100644
--- a/config.m4
+++ b/config.m4
@@ -1,44 +1,26 @@
-dnl $Id$
dnl config.m4 for extension rayaop
-dnl Include PECL configuration macro
-dnl link https://github.com/php/pecl-tools/blob/master/autoconf/pecl.m4
-sinclude(./autoconf/pecl.m4)
+PHP_ARG_ENABLE(rayaop, whether to enable rayaop,
+[ --enable-rayaop Enable rayaop])
-dnl Include macro for detecting PHP executable
-dnl link https://github.com/php/pecl-tools/blob/master/autoconf/php-executable.m4
-sinclude(./autoconf/php-executable.m4)
-
-dnl Initialize PECL extension
-dnl link https://github.com/php/pecl-tools/blob/master/pecl.m4#L229
-PECL_INIT([rayaop])
-
-dnl Add configuration option to enable the extension
-dnl link https://www.gnu.org/software/autoconf/manual/autoconf-2.68/html_node/External-Shell-Variables.html
-PHP_ARG_ENABLE(rayaop, whether to enable rayaop, [ --enable-rayaop Enable rayaop])
-
-dnl Process if the extension is enabled
if test "$PHP_RAYAOP" != "no"; then
dnl Define whether the extension is enabled
- dnl link https://www.gnu.org/software/autoconf/manual/autoconf-2.68/html_node/Defining-Variables.html
AC_DEFINE(HAVE_RAYAOP, 1, [whether rayaop is enabled])
dnl Add new PHP extension
- dnl link https://www.phpinternalsbook.com/build_system/build_system.html
PHP_NEW_EXTENSION(rayaop, rayaop.c, $ext_shared)
dnl Add Makefile fragment
- dnl link https://www.phpinternalsbook.com/build_system/build_system.html#php-add-makefile-fragment
PHP_ADD_MAKEFILE_FRAGMENT
dnl Add instruction to install header files
- dnl link https://www.phpinternalsbook.com/build_system/build_system.html#php-install-headers
PHP_INSTALL_HEADERS([ext/rayaop], [php_rayaop.h])
+ dnl Add quiet mode option
PHP_ARG_ENABLE(rayaop-quiet, whether to suppress experimental notices,
- [ --enable-rayaop-quiet Suppress experimental notices], no, yes)
+ [ --enable-rayaop-quiet Suppress experimental notices], no, yes)
if test "$PHP_RAYAOP_QUIET" != "no"; then
- AC_DEFINE(RAYAOP_QUIET, 1, [Whether to suppress experimental notices])
+ AC_DEFINE(RAYAOP_QUIET, 1, [Whether to suppress experimental notices])
fi
-fi
+fi
\ No newline at end of file
diff --git a/php_rayaop.h b/php_rayaop.h
index 87742f1..6adbdaf 100644
--- a/php_rayaop.h
+++ b/php_rayaop.h
@@ -1,97 +1,155 @@
-/* If PHP_RAYAOP_H is not defined, then define PHP_RAYAOP_H (header guard) */
+/* Header guard */
#ifndef PHP_RAYAOP_H
#define PHP_RAYAOP_H
-/* If HAVE_CONFIG_H is defined, then include config.h */
+// #define RAYAOP_DEBUG 1
+
+/* Configuration header */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
-#include "php.h" /* Include PHP core header file */
-#include "php_ini.h" /* Include PHP INI related header file */
-#include "ext/standard/info.h" /* Include standard extension module information related header */
-#include "zend_exceptions.h" /* Include Zend exception handling related header */
-#include "zend_interfaces.h" /* Include Zend interface related header */
+/* Required system headers first */
+#include
+
+/* PHP Core headers in correct order */
+#include "php.h"
+#include "zend.h"
+#include "zend_types.h"
+#include "zend_API.h"
+#include "zend_ini.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "zend_exceptions.h"
+#include "zend_interfaces.h"
-/* If in thread-safe mode, then include Thread Safe Resource Manager */
#ifdef ZTS
#include "TSRM.h"
#endif
-/* Define RayAOP namespace */
+/* Constants */
+#define MAX_EXECUTION_DEPTH 100
+#define PHP_RAYAOP_VERSION "1.0.0"
#define RAYAOP_NS "Ray\\Aop\\"
-/* Declare rayaop module entry as external reference */
-extern zend_module_entry rayaop_module_entry;
+/* Error codes */
+#define RAYAOP_E_MEMORY_ALLOCATION 1
+#define RAYAOP_E_HASH_UPDATE 2
+#define RAYAOP_E_INVALID_HANDLER 3
+#define RAYAOP_E_MAX_DEPTH_EXCEEDED 4
+
+/* Argument information declarations */
+ZEND_BEGIN_ARG_INFO_EX(arginfo_method_intercept, 0, 0, 3)
+ ZEND_ARG_TYPE_INFO(0, class_name, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO(0, method_name, IS_STRING, 0)
+ ZEND_ARG_OBJ_INFO(0, interceptor, Ray\\Aop\\MethodInterceptorInterface, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_method_intercept_init, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_method_intercept_enable, 0, 1, IS_VOID, 0)
+ ZEND_ARG_TYPE_INFO(0, enable, _IS_BOOL, 0)
+ZEND_END_ARG_INFO()
+
+/* Debug mode configuration */
+#ifdef RAYAOP_DEBUG
+#define PHP_RAYAOP_DEBUG_PRINT(fmt, ...) \
+ do { \
+ php_printf("RAYAOP DEBUG [%s:%d]: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \
+ } while (0)
+#else
+#define PHP_RAYAOP_DEBUG_PRINT(fmt, ...)
+#endif
-/* Define pointer to rayaop module */
+/* Declare mutex type */
+#if defined(WIN32)
+#define MUTEX_T HANDLE
+#else
+#define MUTEX_T pthread_mutex_t*
+#endif
+extern MUTEX_T rayaop_mutex;
+
+#ifdef ZTS
+#define RAYAOP_G_LOCK() tsrm_mutex_lock(rayaop_mutex)
+#define RAYAOP_G_UNLOCK() tsrm_mutex_unlock(rayaop_mutex)
+#else
+#define RAYAOP_G_LOCK()
+#define RAYAOP_G_UNLOCK()
+#endif
+
+/* Module entry */
+extern zend_module_entry rayaop_module_entry;
#define phpext_rayaop_ptr &rayaop_module_entry
-/* If in Windows environment, specify DLL export */
+/* Interface class entry */
+extern zend_class_entry *ray_aop_method_interceptor_interface_ce;
+
+/* Windows DLL export */
#ifdef PHP_WIN32
#define PHP_RAYAOP_API __declspec(dllexport)
-/* If using GCC 4 or later, specify default visibility */
#elif defined(__GNUC__) && __GNUC__ >= 4
#define PHP_RAYAOP_API __attribute__ ((visibility("default")))
-/* For other environments, no specific definition is made */
#else
#define PHP_RAYAOP_API
#endif
-/* If in thread-safe mode, include Thread Safe Resource Manager again (redundant but for safety) */
-#ifdef ZTS
-#include "TSRM.h"
-#endif
-
-/* Macro for debug output */
-#ifdef RAYAOP_DEBUG /* If debug mode is enabled */
-#define PHP_RAYAOP_DEBUG_PRINT(fmt, ...) php_printf("RAYAOP DEBUG: " fmt "\n", ##__VA_ARGS__) /* Define debug output macro */
-#else /* If debug mode is disabled */
-#define PHP_RAYAOP_DEBUG_PRINT(fmt, ...) /* Do nothing */
-#endif
-
-/* Structure to hold intercept information */
+/* Intercept information structure */
typedef struct _php_rayaop_intercept_info {
- zend_string *class_name; /* Class name to intercept */
- zend_string *method_name; /* Method name to intercept */
- zval handler; /* Intercept handler */
+ zend_string *class_name; /* Class name to intercept */
+ zend_string *method_name; /* Method name to intercept */
+ zval handler; /* Intercept handler */
+ zend_bool is_enabled; /* Flag to enable/disable interception */
} php_rayaop_intercept_info;
-/* Function declarations */
-PHP_MINIT_FUNCTION(rayaop); /* Module initialization function */
-PHP_MSHUTDOWN_FUNCTION(rayaop); /* Module shutdown function */
-PHP_RINIT_FUNCTION(rayaop); /* Request initialization function */
-PHP_RSHUTDOWN_FUNCTION(rayaop); /* Request shutdown function */
-PHP_MINFO_FUNCTION(rayaop); /* Module information function */
-PHP_FUNCTION(method_intercept); /* Method intercept function */
-
-/* Utility function declarations */
-void php_rayaop_handle_error(const char *message); /* Error handling function */
-bool php_rayaop_should_intercept(zend_execute_data *execute_data); /* Function to determine if interception is necessary */
-char *php_rayaop_generate_intercept_key(zend_string *class_name, zend_string *method_name, size_t *key_len); /* Function to generate intercept key */
-php_rayaop_intercept_info *php_rayaop_find_intercept_info(const char *key, size_t key_len); /* Function to search for intercept information */
-void php_rayaop_execute_intercept(zend_execute_data *execute_data, php_rayaop_intercept_info *info); /* Function to execute interception */
-void php_rayaop_free_intercept_info(zval *zv); /* Function to free intercept information */
-
-#ifdef RAYAOP_DEBUG /* If debug mode is enabled */
-void php_rayaop_debug_print_zval(zval *value); /* Function to debug print zval value */
-static void php_rayaop_dump_intercept_info(void); /* Function to dump intercept information */
-#endif
-
+/* Module globals structure */
ZEND_BEGIN_MODULE_GLOBALS(rayaop)
- /* Start of rayaop module global variables */
- HashTable *intercept_ht; /* Intercept hash table */
- zend_bool is_intercepting; /* Intercepting flag */
-ZEND_END_MODULE_GLOBALS(rayaop) /* End of rayaop module global variables */
+ HashTable *intercept_ht; /* Intercept hash table */
+ zend_bool is_intercepting; /* Intercepting flag */
+ uint32_t execution_depth; /* Execution depth counter */
+ zend_bool method_intercept_enabled; /* Global interception enable flag */
+ uint32_t debug_level; /* Debug level */
+ZEND_END_MODULE_GLOBALS(rayaop)
-/* If in thread-safe mode, global variable access macro (thread-safe version) */
+/* Global initializer */
+static void php_rayaop_init_globals(zend_rayaop_globals *globals);
+
+/* Globals access macro */
#ifdef ZTS
#define RAYAOP_G(v) TSRMG(rayaop_globals_id, zend_rayaop_globals *, v)
-/* If in non-thread-safe mode, global variable access macro (non-thread-safe version) */
#else
#define RAYAOP_G(v) (rayaop_globals.v)
#endif
+/* Module functions */
+PHP_MINIT_FUNCTION(rayaop);
+PHP_MSHUTDOWN_FUNCTION(rayaop);
+PHP_RINIT_FUNCTION(rayaop);
+PHP_RSHUTDOWN_FUNCTION(rayaop);
+PHP_MINFO_FUNCTION(rayaop);
+
+/* Extension functions */
+PHP_FUNCTION(method_intercept);
+PHP_FUNCTION(method_intercept_init);
+PHP_FUNCTION(method_intercept_enable);
+
+/* Utility functions */
+PHP_RAYAOP_API void php_rayaop_handle_error(int error_code, const char *message);
+PHP_RAYAOP_API bool php_rayaop_should_intercept(zend_execute_data *execute_data);
+PHP_RAYAOP_API char *php_rayaop_generate_key(zend_string *class_name, zend_string *method_name, size_t *key_len);
+PHP_RAYAOP_API php_rayaop_intercept_info *php_rayaop_find_intercept_info(const char *key, size_t key_len);
+
+/* Memory management functions */
+PHP_RAYAOP_API void php_rayaop_free_intercept_info(zval *zv);
+PHP_RAYAOP_API php_rayaop_intercept_info *php_rayaop_create_intercept_info(void);
+
+/* Debug functions */
+#ifdef RAYAOP_DEBUG
+void php_rayaop_debug_dump_intercept_info(void);
+void php_rayaop_debug_print_zval(zval *value);
+#endif
+
+/* Module globals declaration */
ZEND_EXTERN_MODULE_GLOBALS(rayaop)
-#endif /* End of header guard */
+#endif /* PHP_RAYAOP_H */
\ No newline at end of file
diff --git a/rayaop.c b/rayaop.c
index f8fff18..31c85b6 100644
--- a/rayaop.c
+++ b/rayaop.c
@@ -1,478 +1,498 @@
-#ifdef HAVE_CONFIG_H
-#include "config.h" /* Include configuration file */
+#ifdef ZTS
+MUTEX_T rayaop_mutex;
#endif
-#define PHP_RAYAOP_VERSION "0.1.0"
-
-#ifndef RAYAOP_QUIET
-#define RAYAOP_QUIET 0
+#ifdef HAVE_CONFIG_H
+#include "config.h"
#endif
-// #define RAYAOP_DEBUG
-
-#include "php_rayaop.h" /* Include header file for RayAOP extension */
+#include "php_rayaop.h"
-/* Declaration of module global variables */
+/* Module globals initialization */
ZEND_DECLARE_MODULE_GLOBALS(rayaop)
-/* Declaration of static variable: pointer to the original zend_execute_ex function */
-static void (*php_rayaop_original_execute_ex)(zend_execute_data *execute_data);
-
-/* {{{ proto void php_rayaop_init_globals(zend_rayaop_globals *rayaop_globals)
- Global initialization function
-
- This function initializes the global variables for the RayAOP extension.
+/* Global variable declarations */
+zend_class_entry *ray_aop_method_interceptor_interface_ce;
+static void (*php_rayaop_original_execute_ex)(zend_execute_data *execute_data) = NULL;
- @param zend_rayaop_globals *rayaop_globals Pointer to global variables
-*/
-static void php_rayaop_init_globals(zend_rayaop_globals *rayaop_globals) {
- rayaop_globals->intercept_ht = NULL; /* Initialize intercept hash table */
- rayaop_globals->is_intercepting = 0; /* Initialize intercept flag */
-}
-/* }}} */
+/* Argument information for the interceptor method */
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ray_aop_method_interceptor_intercept, 0, 3, IS_MIXED, 0)
+ ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0)
+ ZEND_ARG_TYPE_INFO(0, method, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO(0, params, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
-/* Macro for debug output */
-#ifdef RAYAOP_DEBUG
-#define PHP_RAYAOP_DEBUG_PRINT(fmt, ...) php_printf("RAYAOP DEBUG: " fmt "\n", ##__VA_ARGS__) /* Output debug information */
-#else
-#define PHP_RAYAOP_DEBUG_PRINT(fmt, ...) /* Do nothing if not in debug mode */
-#endif
+/* Interface methods */
+static zend_function_entry ray_aop_method_interceptor_interface_methods[] = {
+ PHP_ABSTRACT_ME(Ray_Aop_MethodInterceptorInterface, intercept, arginfo_ray_aop_method_interceptor_intercept)
+ PHP_FE_END
+};
-/* Argument information for method_intercept function */
-ZEND_BEGIN_ARG_INFO_EX(arginfo_method_intercept, 0, 0, 3)
- ZEND_ARG_TYPE_INFO(0, class_name, IS_STRING, 0) /* Argument information for class name */
- ZEND_ARG_TYPE_INFO(0, method_name, IS_STRING, 0) /* Argument information for method name */
- ZEND_ARG_OBJ_INFO(0, interceptor, Ray\\Aop\\MethodInterceptorInterface, 0) /* Argument information for intercept handler */
-ZEND_END_ARG_INFO()
+/* Module function declarations */
+static const zend_function_entry rayaop_functions[] = {
+ PHP_FE(method_intercept, arginfo_method_intercept)
+ PHP_FE(method_intercept_init, arginfo_method_intercept_init)
+ PHP_FE(method_intercept_enable, arginfo_method_intercept_enable)
+ PHP_FE_END
+};
-/* {{{ proto void php_rayaop_handle_error(const char *message)
- Error handling function
- This function handles errors by outputting an error message.
- @param const char *message Error message to be displayed
-*/
-void php_rayaop_handle_error(const char *message) {
- php_error_docref(NULL, E_ERROR, "Memory error: %s", message); /* Output error message */
+/* Module globals initializer */
+static void php_rayaop_init_globals(zend_rayaop_globals *globals) {
+ globals->intercept_ht = NULL;
+ globals->is_intercepting = 0;
+ globals->execution_depth = 0;
+ globals->method_intercept_enabled = 0;
+ globals->debug_level = 0;
}
-/* }}} */
-/* {{{ proto bool php_rayaop_should_intercept(zend_execute_data *execute_data)
- Function to determine if interception is necessary
+/* Error handling function */
+PHP_RAYAOP_API void php_rayaop_handle_error(int error_code, const char *message) {
+ switch (error_code) {
+ case RAYAOP_E_MEMORY_ALLOCATION:
+ php_error_docref(NULL, E_ERROR, "Memory allocation failed: %s", message);
+ break;
+ case RAYAOP_E_HASH_UPDATE:
+ php_error_docref(NULL, E_ERROR, "Hash table update failed: %s", message);
+ break;
+ case RAYAOP_E_INVALID_HANDLER:
+ php_error_docref(NULL, E_WARNING, "Invalid handler: %s", message);
+ break;
+ case RAYAOP_E_MAX_DEPTH_EXCEEDED:
+ php_error_docref(NULL, E_WARNING, "Maximum execution depth exceeded: %s", message);
+ break;
+ default:
+ php_error_docref(NULL, E_ERROR, "RayAOP Error (%d): %s", error_code, message);
+ }
+}
- This function checks if the current execution should be intercepted.
+/* Memory management functions */
+PHP_RAYAOP_API php_rayaop_intercept_info *php_rayaop_create_intercept_info(void) {
+ php_rayaop_intercept_info *info = ecalloc(1, sizeof(php_rayaop_intercept_info));
+ if (!info) {
+ php_rayaop_handle_error(RAYAOP_E_MEMORY_ALLOCATION, "Failed to allocate intercept info");
+ return NULL;
+ }
+ info->is_enabled = 1;
+ return info;
+}
- @param zend_execute_data *execute_data Execution data
- @return bool Returns true if interception is necessary, false otherwise
-*/
-bool php_rayaop_should_intercept(zend_execute_data *execute_data) {
- return execute_data->func->common.scope && /* Scope exists */
- execute_data->func->common.function_name && /* Function name exists */
- !RAYAOP_G(is_intercepting); /* Not currently intercepting */
+PHP_RAYAOP_API void php_rayaop_free_intercept_info(zval *zv) {
+ RAYAOP_G_LOCK();
+ php_rayaop_intercept_info *info = Z_PTR_P(zv);
+ if (info) {
+ if (info->class_name) {
+ zend_string_release(info->class_name);
+ }
+ if (info->method_name) {
+ zend_string_release(info->method_name);
+ }
+ zval_ptr_dtor(&info->handler);
+ efree(info);
+ }
+ RAYAOP_G_UNLOCK();
}
-/* }}} */
-/* {{{ proto char* php_rayaop_generate_intercept_key(zend_string *class_name, zend_string *method_name, size_t *key_len)
- Function to generate intercept key
+/* Interception helper functions */
+PHP_RAYAOP_API bool php_rayaop_should_intercept(zend_execute_data *execute_data) {
+ if (!RAYAOP_G(method_intercept_enabled)) {
+ return false;
+ }
+
+ if (RAYAOP_G(execution_depth) >= MAX_EXECUTION_DEPTH) {
+ php_rayaop_handle_error(RAYAOP_E_MAX_DEPTH_EXCEEDED, "Maximum execution depth reached");
+ return false;
+ }
- This function creates a key in the format "class_name::method_name" for use in the intercept hash table.
+ return execute_data &&
+ execute_data->func &&
+ execute_data->func->common.scope &&
+ execute_data->func->common.function_name &&
+ !RAYAOP_G(is_intercepting);
+}
- @param zend_string *class_name The name of the class
- @param zend_string *method_name The name of the method
- @param size_t *key_len Pointer to store the length of the generated key
- @return char* The generated key, which must be freed by the caller using efree()
-*/
-char *php_rayaop_generate_intercept_key(zend_string *class_name, zend_string *method_name, size_t *key_len) {
+PHP_RAYAOP_API char *php_rayaop_generate_key(zend_string *class_name, zend_string *method_name, size_t *key_len) {
char *key = NULL;
*key_len = spprintf(&key, 0, "%s::%s", ZSTR_VAL(class_name), ZSTR_VAL(method_name));
- /* Generate key in the format class_name::method_name */
- PHP_RAYAOP_DEBUG_PRINT("Generated key: %s", key); /* Output generated key for debugging */
+ PHP_RAYAOP_DEBUG_PRINT("Generated key: %s", key);
return key;
}
-/* }}} */
-
-/* {{{ proto php_rayaop_intercept_info* php_rayaop_find_intercept_info(const char *key, size_t key_len)
- Function to search for intercept information
- This function searches for intercept information in the hash table using the provided key.
-
- @param const char *key The key to search for
- @param size_t key_len The length of the key
- @return php_rayaop_intercept_info* Pointer to the intercept information if found, NULL otherwise
-*/
-php_rayaop_intercept_info *php_rayaop_find_intercept_info(const char *key, size_t key_len) {
- return zend_hash_str_find_ptr(RAYAOP_G(intercept_ht), key, key_len);
- /* Search for intercept information in hash table */
+PHP_RAYAOP_API php_rayaop_intercept_info *php_rayaop_find_intercept_info(const char *key, size_t key_len) {
+ RAYAOP_G_LOCK();
+ php_rayaop_intercept_info *info = NULL;
+ if (RAYAOP_G(intercept_ht)) {
+ info = zend_hash_str_find_ptr(RAYAOP_G(intercept_ht), key, key_len);
+ }
+ RAYAOP_G_UNLOCK();
+ return info;
}
-/* }}} */
-/* {{{ Helper function to prepare intercept parameters */
-static void prepare_intercept_params(zend_execute_data *execute_data, zval *params, php_rayaop_intercept_info *info) {
+/* Parameter preparation and cleanup */
+static bool prepare_intercept_params(zend_execute_data *execute_data, zval *params, php_rayaop_intercept_info *info) {
+ if (!execute_data->This.value.obj) {
+ php_rayaop_handle_error(RAYAOP_E_INVALID_HANDLER, "Object instance is NULL");
+ return false;
+ }
+
ZVAL_OBJ(¶ms[0], execute_data->This.value.obj);
- ZVAL_STR(¶ms[1], info->method_name);
+ ZVAL_STR_COPY(¶ms[1], info->method_name);
array_init(¶ms[2]);
uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data);
- zval *args = ZEND_CALL_ARG(execute_data, 1);
- for (uint32_t i = 0; i < arg_count; i++) {
- zval *arg = &args[i];
- Z_TRY_ADDREF_P(arg);
- add_next_index_zval(¶ms[2], arg);
+ if (arg_count > 0) {
+ zval *args = ZEND_CALL_ARG(execute_data, 1);
+ for (uint32_t i = 0; i < arg_count; i++) {
+ zval *arg = &args[i];
+ if (!Z_ISUNDEF_P(arg)) {
+ Z_TRY_ADDREF_P(arg);
+ add_next_index_zval(¶ms[2], arg);
+ }
+ }
}
+ return true;
}
-/* }}} */
-
-/* {{{ Helper function to call the intercept handler */
-static bool call_intercept_handler(zval *handler, zval *params, zval *retval) {
- zval func_name;
- ZVAL_STRING(&func_name, "intercept");
-
- bool success = (call_user_function(NULL, handler, &func_name, retval, 3, params) == SUCCESS);
-
- zval_ptr_dtor(&func_name);
- return success;
-}
-/* }}} */
-/* {{{ Helper function to clean up after interception */
-static void cleanup_intercept(zval *params, zval *retval) {
+static void cleanup_intercept_params(zval *params) {
zval_ptr_dtor(¶ms[1]);
zval_ptr_dtor(¶ms[2]);
- zval_ptr_dtor(retval);
}
-/* }}} */
-
-/* {{{ proto void php_rayaop_execute_intercept(zend_execute_data *execute_data, php_rayaop_intercept_info *info)
- Main function to execute method interception */
-void php_rayaop_execute_intercept(zend_execute_data *execute_data, php_rayaop_intercept_info *info) {
- PHP_RAYAOP_DEBUG_PRINT("Executing intercept for %s::%s", ZSTR_VAL(info->class_name), ZSTR_VAL(info->method_name));
-
- if (Z_TYPE(info->handler) != IS_OBJECT) {
- PHP_RAYAOP_DEBUG_PRINT("Invalid handler, skipping interception");
- return;
- }
-
- if (execute_data->This.value.obj == NULL) {
- PHP_RAYAOP_DEBUG_PRINT("Object is NULL, calling original function");
- php_rayaop_original_execute_ex(execute_data);
- return;
- }
- zval retval, params[3];
- prepare_intercept_params(execute_data, params, info);
+/* Interception execution */
+static bool execute_intercept_handler(zval *handler, zval *params, zval *retval) {
+ zval method_name;
+ ZVAL_STRING(&method_name, "intercept");
- RAYAOP_G(is_intercepting) = 1;
+ bool success = (call_user_function(NULL, handler, &method_name, retval, 3, params) == SUCCESS);
+ zval_ptr_dtor(&method_name);
- ZVAL_UNDEF(&retval);
- if (call_intercept_handler(&info->handler, params, &retval)) {
- if (!Z_ISUNDEF(retval)) {
- ZVAL_COPY(execute_data->return_value, &retval);
- }
- } else {
- php_error_docref(NULL, E_WARNING, "Interception failed for %s::%s", ZSTR_VAL(info->class_name), ZSTR_VAL(info->method_name));
+ if (!success) {
+ php_rayaop_handle_error(RAYAOP_E_INVALID_HANDLER, "Failed to execute intercept handler");
}
- cleanup_intercept(params, &retval);
- RAYAOP_G(is_intercepting) = 0;
-
- PHP_RAYAOP_DEBUG_PRINT("Interception completed for %s::%s", ZSTR_VAL(info->class_name), ZSTR_VAL(info->method_name));
-}
-/* }}} */
-
-/* {{{ proto void php_rayaop_free_intercept_info(zval *zv)
- Function to free intercept information
-
- This function frees the memory allocated for intercept information.
-
- @param zval *zv The zval containing the intercept information to be freed
-*/
-void php_rayaop_free_intercept_info(zval *zv) {
- php_rayaop_intercept_info *info = Z_PTR_P(zv); /* Get intercept information pointer from zval */
- if (info) {
- /* If intercept information exists */
- PHP_RAYAOP_DEBUG_PRINT("Freeing intercept info for %s::%s", ZSTR_VAL(info->class_name), ZSTR_VAL(info->method_name));
- /* Output debug information */
- zend_string_release(info->class_name); /* Free memory for class name */
- zend_string_release(info->method_name); /* Free memory for method name */
- zval_ptr_dtor(&info->handler); /* Free memory for handler */
- efree(info); /* Free memory for intercept information structure */
- }
+ return success;
}
-/* }}} */
-
-/* {{{ proto void php_rayaop_execute_ex(zend_execute_data *execute_data)
- Custom zend_execute_ex function
-
- This function replaces the original zend_execute_ex function to implement method interception.
-
- @param zend_execute_data *execute_data The execution data
-*/
-static void php_rayaop_execute_ex(zend_execute_data *execute_data) {
- PHP_RAYAOP_DEBUG_PRINT("php_rayaop_execute_ex called"); /* Output debug information */
+/* Fixed rayaop_execute_ex function */
+static void rayaop_execute_ex(zend_execute_data *execute_data) {
if (!php_rayaop_should_intercept(execute_data)) {
- /* If interception is not necessary */
- php_rayaop_original_execute_ex(execute_data); /* Call the original execution function */
- return; /* End processing */
+ if (php_rayaop_original_execute_ex) {
+ php_rayaop_original_execute_ex(execute_data);
+ } else {
+ zend_execute_ex(execute_data);
+ }
+ return;
}
- zend_function *current_function = execute_data->func; /* Get current function information */
- zend_string *class_name = current_function->common.scope->name; /* Get class name */
- zend_string *method_name = current_function->common.function_name; /* Get method name */
+ RAYAOP_G(execution_depth)++;
+ PHP_RAYAOP_DEBUG_PRINT("Execution depth: %d", RAYAOP_G(execution_depth));
+ zend_function *func = execute_data->func;
size_t key_len;
- char *key = php_rayaop_generate_intercept_key(class_name, method_name, &key_len); /* Generate intercept key */
+ char *key = php_rayaop_generate_key(func->common.scope->name, func->common.function_name, &key_len);
- php_rayaop_intercept_info *info = php_rayaop_find_intercept_info(key, key_len); /* Search for intercept information */
+ php_rayaop_intercept_info *info = php_rayaop_find_intercept_info(key, key_len);
+ if (info && info->is_enabled) {
+ PHP_RAYAOP_DEBUG_PRINT("Executing intercept for %s", key);
- if (info) {
- /* If intercept information is found */
- PHP_RAYAOP_DEBUG_PRINT("Found intercept info for key: %s", key); /* Output debug information */
- php_rayaop_execute_intercept(execute_data, info); /* Execute interception */
+ if (Z_TYPE(info->handler) != IS_OBJECT) {
+ php_rayaop_handle_error(RAYAOP_E_INVALID_HANDLER, "Invalid interceptor type");
+ if (php_rayaop_original_execute_ex) {
+ php_rayaop_original_execute_ex(execute_data);
+ }
+ } else {
+ zval retval;
+ zval params[3];
+
+ prepare_intercept_params(execute_data, params, info);
+ if (!prepare_intercept_params(execute_data, params, info)) {
+ cleanup_intercept_params(params);
+ RAYAOP_G(is_intercepting) = 0;
+ if (php_rayaop_original_execute_ex) {
+ php_rayaop_original_execute_ex(execute_data);
+ } else {
+ zend_execute_ex(execute_data);
+ }
+ efree(key);
+ RAYAOP_G(execution_depth)--;
+ return;
+ }
+ RAYAOP_G(is_intercepting) = 1;
+
+ ZVAL_UNDEF(&retval);
+ if (execute_intercept_handler(&info->handler, params, &retval)) {
+ if (!Z_ISUNDEF(retval) && execute_data->return_value) {
+ ZVAL_COPY(execute_data->return_value, &retval);
+ }
+ zval_ptr_dtor(&retval);
+ }
+
+ cleanup_intercept_params(params);
+ RAYAOP_G(is_intercepting) = 0;
+ }
} else {
- /* If intercept information is not found */
- PHP_RAYAOP_DEBUG_PRINT("No intercept info found for key: %s", key); /* Output debug information */
- php_rayaop_original_execute_ex(execute_data); /* Call the original execution function */
+ if (php_rayaop_original_execute_ex) {
+ php_rayaop_original_execute_ex(execute_data);
+ }
}
- efree(key); /* Free memory for key */
+ efree(key);
+ RAYAOP_G(execution_depth)--;
}
-/* }}} */
-
-/* {{{ proto void php_rayaop_hash_update_failed(php_rayaop_intercept_info *new_info, char *key)
- Handling for hash table update failure
-
- This function handles the case when updating the intercept hash table fails.
-
- @param php_rayaop_intercept_info *new_info The new intercept information that failed to be added
- @param char *key The key that was used for the failed update
-*/
-void php_rayaop_hash_update_failed(php_rayaop_intercept_info *new_info, char *key) {
- php_rayaop_handle_error("Failed to update intercept hash table"); /* Output error message */
- zend_string_release(new_info->class_name); /* Free memory for class name */
- zend_string_release(new_info->method_name); /* Free memory for method name */
- zval_ptr_dtor(&new_info->handler); /* Free memory for handler */
- efree(new_info); /* Free memory for intercept information structure */
- efree(key); /* Free memory for key */
-}
-/* }}} */
-
-/* {{{ proto bool method_intercept(string class_name, string method_name, object intercepted)
- Function to register intercept method
-
- This function registers an interceptor for a specified class method.
-
- @param string class_name The name of the class
- @param string method_name The name of the method
- @param object intercepted The interceptor object
- @return bool Returns TRUE on success or FALSE on failure
-*/
+/* Implementation of method_intercept function */
PHP_FUNCTION(method_intercept) {
- PHP_RAYAOP_DEBUG_PRINT("method_intercept called"); /* Output debug information */
-
- char *class_name, *method_name; /* Pointers for class name and method name */
- size_t class_name_len, method_name_len; /* Lengths of class name and method name */
- zval *intercepted; /* Intercept handler */
+ char *class_name, *method_name;
+ size_t class_name_len, method_name_len;
+ zval *interceptor;
ZEND_PARSE_PARAMETERS_START(3, 3)
- Z_PARAM_STRING(class_name, class_name_len) /* Parse class name parameter */
- Z_PARAM_STRING(method_name, method_name_len) /* Parse method name parameter */
- Z_PARAM_OBJECT(intercepted) /* Parse intercept handler parameter */
+ Z_PARAM_STRING(class_name, class_name_len)
+ Z_PARAM_STRING(method_name, method_name_len)
+ Z_PARAM_OBJECT(interceptor)
ZEND_PARSE_PARAMETERS_END();
- php_rayaop_intercept_info *new_info = ecalloc(1, sizeof(php_rayaop_intercept_info)); /* Allocate memory for new intercept information */
- if (!new_info) {
- /* If memory allocation fails */
- php_rayaop_handle_error("Failed to allocate memory for intercept_info"); /* Output error message */
- RETURN_FALSE; /* Return false and end */
+ php_rayaop_intercept_info *info = php_rayaop_create_intercept_info();
+ if (!info) {
+ RETURN_FALSE;
}
- new_info->class_name = zend_string_init(class_name, class_name_len, 0); /* Initialize class name */
- new_info->method_name = zend_string_init(method_name, method_name_len, 0); /* Initialize method name */
- ZVAL_COPY(&new_info->handler, intercepted); /* Copy intercept handler */
-
- char *key = NULL;
- size_t key_len = spprintf(&key, 0, "%s::%s", class_name, method_name); /* Generate intercept key */
+ info->class_name = zend_string_init(class_name, class_name_len, 0);
+ info->method_name = zend_string_init(method_name, method_name_len, 0);
+ ZVAL_COPY(&info->handler, interceptor);
- if (zend_hash_str_update_ptr(RAYAOP_G(intercept_ht), key, key_len, new_info) == NULL) {
- /* Add to hash table */
- php_rayaop_hash_update_failed(new_info, key); /* Execute error handling if addition fails */
-RETURN_FALSE; /* Return false and end */
+ char *key;
+ size_t key_len;
+ key = php_rayaop_generate_key(info->class_name, info->method_name, &key_len);
+
+ RAYAOP_G_LOCK();
+ if (zend_hash_str_update_ptr(RAYAOP_G(intercept_ht), key, key_len, info) == NULL) {
+ RAYAOP_G_UNLOCK();
+ efree(key);
+ zval tmp_zv;
+ ZVAL_PTR(&tmp_zv, info);
+ php_rayaop_free_intercept_info(&tmp_zv);
+ php_rayaop_handle_error(RAYAOP_E_HASH_UPDATE, "Failed to update intercept hash table");
+ RETURN_FALSE;
}
+ RAYAOP_G_UNLOCK();
- efree(key); /* Free memory for key */
- PHP_RAYAOP_DEBUG_PRINT("Successfully registered intercept info"); /* Output debug information */
- RETURN_TRUE; /* Return true and end */
+ efree(key);
+ RETURN_TRUE;
}
-/* }}} */
-/* Interface definition */
-zend_class_entry *ray_aop_method_interceptor_interface_ce;
-
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ray_aop_method_interceptor_intercept, 0, 3, IS_MIXED, 0)
- ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0) /* 1st argument: object */
- ZEND_ARG_TYPE_INFO(0, method, IS_STRING, 0) /* 2nd argument: method name */
- ZEND_ARG_TYPE_INFO(0, params, IS_ARRAY, 0) /* 3rd argument: parameter array */
-ZEND_END_ARG_INFO()
-
-static const zend_function_entry ray_aop_method_interceptor_interface_methods[] = {
- ZEND_ABSTRACT_ME(Ray_Aop_MethodInterceptorInterface, intercept, arginfo_ray_aop_method_interceptor_intercept)
- /* Define intercept method */
- PHP_FE_END /* End of function entries */
-};
+/* method_intercept_init function fix */
+PHP_FUNCTION(method_intercept_init) {
+ RAYAOP_G_LOCK();
+ if (RAYAOP_G(intercept_ht)) {
+ zend_hash_clean(RAYAOP_G(intercept_ht));
+ } else {
+ ALLOC_HASHTABLE(RAYAOP_G(intercept_ht));
+ if (!RAYAOP_G(intercept_ht)) {
+ RAYAOP_G_UNLOCK();
+ php_rayaop_handle_error(RAYAOP_E_MEMORY_ALLOCATION, "Failed to allocate intercept hash table");
+ RETURN_FALSE;
+ }
+ zend_hash_init(RAYAOP_G(intercept_ht), 8, NULL, (dtor_func_t)php_rayaop_free_intercept_info, 0);
+ }
+ RAYAOP_G_UNLOCK();
+ RETURN_TRUE;
+}
-/* {{{ proto int PHP_MINIT_FUNCTION(rayaop)
- Extension initialization function
+/* Implementation of method_intercept_enable function */
+PHP_FUNCTION(method_intercept_enable) {
+ zend_bool enable;
+ ZEND_PARSE_PARAMETERS_START(1, 1)
+ Z_PARAM_BOOL(enable)
+ ZEND_PARSE_PARAMETERS_END();
- This function is called when the extension is loaded. It initializes the extension,
- registers the interface, and sets up the custom execute function.
+ RAYAOP_G_LOCK();
+ RAYAOP_G(method_intercept_enabled) = enable;
+ if (enable) {
+ zend_execute_ex = rayaop_execute_ex;
+ PHP_RAYAOP_DEBUG_PRINT("Method intercept enabled");
+ } else {
+ zend_execute_ex = php_rayaop_original_execute_ex;
+ PHP_RAYAOP_DEBUG_PRINT("Method intercept disabled");
+ }
+ RAYAOP_G_UNLOCK();
+}
- @return int Returns SUCCESS on successful initialization
-*/
+/* Module initialization */
PHP_MINIT_FUNCTION(rayaop) {
- PHP_RAYAOP_DEBUG_PRINT("PHP_MINIT_FUNCTION called"); /* Output debug information */
-
#ifdef ZTS
- ts_allocate_id(&rayaop_globals_id, sizeof(zend_rayaop_globals), (ts_allocate_ctor) php_rayaop_init_globals, NULL); /* Initialize global variables in thread-safe mode */
+ /* First, allocate TSRMG */
+ ts_allocate_id(&rayaop_globals_id, sizeof(zend_rayaop_globals),
+ (ts_allocate_ctor)php_rayaop_init_globals, NULL);
+
+ /* Then initialize mutex */
+ rayaop_mutex = tsrm_mutex_alloc();
+ if (!rayaop_mutex) {
+ php_error_docref(NULL, E_ERROR, "Failed to allocate mutex for RayAOP");
+ return FAILURE;
+ }
#else
- php_rayaop_init_globals(&rayaop_globals); /* Initialize global variables in non-thread-safe mode */
-#endif
-
-#if PHP_RAYAOP_EXPERIMENTAL && !RAYAOP_QUIET
- php_error_docref(NULL, E_NOTICE, "The Ray.Aop extension is experimental. Its functions may change or be removed in future releases.");
+ php_rayaop_init_globals(&rayaop_globals);
#endif
+ // Register the interface
zend_class_entry ce;
- INIT_CLASS_ENTRY(ce, "Ray\\Aop\\MethodInterceptorInterface", ray_aop_method_interceptor_interface_methods);
- /* Initialize class entry */
- ray_aop_method_interceptor_interface_ce = zend_register_internal_interface(&ce); /* Register interface */
+ INIT_NS_CLASS_ENTRY(ce, "Ray\\Aop", "MethodInterceptorInterface", ray_aop_method_interceptor_interface_methods);
+ ray_aop_method_interceptor_interface_ce = zend_register_internal_interface(&ce);
- php_rayaop_original_execute_ex = zend_execute_ex; /* Save the original zend_execute_ex function */
- zend_execute_ex = php_rayaop_execute_ex; /* Set the custom zend_execute_ex function */
+ // Initialize other settings
+ RAYAOP_G(method_intercept_enabled) = 1;
+ RAYAOP_G(debug_level) = 0;
+ php_rayaop_original_execute_ex = zend_execute_ex;
+ zend_execute_ex = rayaop_execute_ex;
- PHP_RAYAOP_DEBUG_PRINT("RayAOP extension initialized"); /* Output debug information */
- return SUCCESS; /* Return success */
+ return SUCCESS;
}
-/* }}} */
-
-/* {{{ proto int PHP_MSHUTDOWN_FUNCTION(rayaop)
- Extension shutdown function
- This function is called when the extension is unloaded. It restores the original
- execute function and performs any necessary cleanup.
- @return int Returns SUCCESS on successful shutdown
-*/
+/* Module shutdown */
PHP_MSHUTDOWN_FUNCTION(rayaop) {
- PHP_RAYAOP_DEBUG_PRINT("RayAOP PHP_MSHUTDOWN_FUNCTION called"); /* Output debug information */
- zend_execute_ex = php_rayaop_original_execute_ex; /* Restore the original zend_execute_ex function */
- php_rayaop_original_execute_ex = NULL; /* Clear the saved pointer */
- PHP_RAYAOP_DEBUG_PRINT("RayAOP PHP_MSHUTDOWN_FUNCTION shut down"); /* Output debug information */
- return SUCCESS; /* Return shutdown success */
-}
-/* }}} */
-
-/* {{{ proto int PHP_RINIT_FUNCTION(rayaop)
- Request initialization function
+#ifdef ZTS
+ /* Free mutex */
+ if (rayaop_mutex) {
+ tsrm_mutex_free(rayaop_mutex);
+ rayaop_mutex = NULL;
+ }
+#endif
- This function is called at the beginning of each request. It initializes
- the intercept hash table for the current request.
+ if (php_rayaop_original_execute_ex) {
+ zend_execute_ex = php_rayaop_original_execute_ex;
+ }
+ return SUCCESS;
+}
- @return int Returns SUCCESS on successful initialization
-*/
PHP_RINIT_FUNCTION(rayaop) {
- PHP_RAYAOP_DEBUG_PRINT("PHP_RINIT_FUNCTION called"); /* Output debug information */
- if (RAYAOP_G(intercept_ht) == NULL) {
- /* If intercept hash table is not initialized */
- ALLOC_HASHTABLE(RAYAOP_G(intercept_ht)); /* Allocate memory for hash table */
- zend_hash_init(RAYAOP_G(intercept_ht), 8, NULL, php_rayaop_free_intercept_info, 0); /* Initialize hash table */
+ if (!php_rayaop_original_execute_ex) {
+ php_rayaop_original_execute_ex = zend_execute_ex;
}
- RAYAOP_G(is_intercepting) = 0; /* Initialize intercept flag */
- return SUCCESS; /* Return success */
-}
-/* }}} */
-/* {{{ proto int PHP_RSHUTDOWN_FUNCTION(rayaop)
- Request shutdown function
+ RAYAOP_G_LOCK();
+ if (!RAYAOP_G(intercept_ht)) {
+ ALLOC_HASHTABLE(RAYAOP_G(intercept_ht));
+ if (!RAYAOP_G(intercept_ht)) {
+ RAYAOP_G_UNLOCK();
+ php_rayaop_handle_error(RAYAOP_E_MEMORY_ALLOCATION, "Failed to allocate intercept hash table");
+ return FAILURE;
+ }
+ zend_hash_init(RAYAOP_G(intercept_ht), 8, NULL, (dtor_func_t)php_rayaop_free_intercept_info, 0);
+ }
+ RAYAOP_G(is_intercepting) = 0;
+ RAYAOP_G(execution_depth) = 0;
+
+ if (RAYAOP_G(method_intercept_enabled)) {
+ zend_execute_ex = rayaop_execute_ex;
+ }
- This function is called at the end of each request. It cleans up the
- intercept hash table and frees associated memory.
+ RAYAOP_G_UNLOCK();
+ return SUCCESS;
+}
- @return int Returns SUCCESS on successful shutdown
-*/
+/* Request shutdown */
PHP_RSHUTDOWN_FUNCTION(rayaop) {
- PHP_RAYAOP_DEBUG_PRINT("RayAOP PHP_RSHUTDOWN_FUNCTION called"); /* Output debug information */
+ RAYAOP_G_LOCK();
if (RAYAOP_G(intercept_ht)) {
- /* If intercept hash table exists */
- zend_hash_destroy(RAYAOP_G(intercept_ht)); /* Destroy hash table */
- FREE_HASHTABLE(RAYAOP_G(intercept_ht)); /* Free memory for hash table */
- RAYAOP_G(intercept_ht) = NULL; /* Set hash table pointer to NULL */
+ zend_hash_destroy(RAYAOP_G(intercept_ht));
+ FREE_HASHTABLE(RAYAOP_G(intercept_ht));
+ RAYAOP_G(intercept_ht) = NULL;
}
- PHP_RAYAOP_DEBUG_PRINT("RayAOP PHP_RSHUTDOWN_FUNCTION shut down"); /* Output debug information */
- return SUCCESS; /* Return shutdown success */
+ RAYAOP_G_UNLOCK();
+ return SUCCESS;
}
-/* }}} */
-/* {{{ proto void PHP_MINFO_FUNCTION(rayaop)
- Extension information display function
-
- This function is called to display information about the extension in phpinfo().
-*/
+/* Module info */
PHP_MINFO_FUNCTION(rayaop) {
- php_info_print_table_start(); /* Start information table */
- php_info_print_table_header(2, "rayaop support", "enabled"); /* Display table header */
- php_info_print_table_row(2, "Version", PHP_RAYAOP_VERSION); /* Display version information */
- php_info_print_table_end(); /* End information table */
+ php_info_print_table_start();
+ php_info_print_table_header(2, "RayAOP Support", "enabled");
+ php_info_print_table_row(2, "Version", PHP_RAYAOP_VERSION);
+ php_info_print_table_row(2, "Debug Level",
+ RAYAOP_G(debug_level) == 0 ? "None" :
+ RAYAOP_G(debug_level) == 1 ? "Basic" : "Verbose");
+ php_info_print_table_row(2, "Method Intercept",
+ RAYAOP_G(method_intercept_enabled) ? "Enabled" : "Disabled");
+ php_info_print_table_end();
}
-/* }}} */
#ifdef RAYAOP_DEBUG
-/* {{{ proto void php_rayaop_dump_intercept_info(void)
- Function to dump intercept information (for debugging)
-
- This function outputs debug information about the current state of the intercept hash table.
-*/
-static void php_rayaop_dump_intercept_info(void)
-{
- PHP_RAYAOP_DEBUG_PRINT("Dumping intercept information:"); /* Output debug information */
- if (RAYAOP_G(intercept_ht)) { /* If intercept hash table exists */
- zend_string *key;
+/* Debug functions */
+void php_rayaop_debug_print_zval(zval *value) {
+ if (!value) {
+ php_printf("NULL\n");
+ return;
+ }
+
+ switch (Z_TYPE_P(value)) {
+ case IS_NULL:
+ php_printf("NULL\n");
+ break;
+ case IS_TRUE:
+ php_printf("bool(true)\n");
+ break;
+ case IS_FALSE:
+ php_printf("bool(false)\n");
+ break;
+ case IS_LONG:
+ php_printf("int(%ld)\n", Z_LVAL_P(value));
+ break;
+ case IS_DOUBLE:
+ php_printf("float(%g)\n", Z_DVAL_P(value));
+ break;
+ case IS_STRING:
+ php_printf("string(%d) \"%s\"\n", Z_STRLEN_P(value), Z_STRVAL_P(value));
+ break;
+ case IS_ARRAY:
+ php_printf("array(%d) {...}\n", zend_hash_num_elements(Z_ARRVAL_P(value)));
+ break;
+ case IS_OBJECT:
+ php_printf("object(%s)#%d {...}\n",
+ Z_OBJCE_P(value)->name->val, Z_OBJ_HANDLE_P(value));
+ break;
+ default:
+ php_printf("unknown type(%d)\n", Z_TYPE_P(value));
+ }
+}
+
+void php_rayaop_debug_dump_intercept_info(void) {
+ RAYAOP_G_LOCK();
+ if (RAYAOP_G(intercept_ht)) {
+ php_printf("=== Intercept Information Dump ===\n");
php_rayaop_intercept_info *info;
- ZEND_HASH_FOREACH_STR_KEY_PTR(RAYAOP_G(intercept_ht), key, info) { /* For each element in the hash table */
- if (key && info) { /* If key and information exist */
- PHP_RAYAOP_DEBUG_PRINT("Key: %s", ZSTR_VAL(key)); /* Output key */
- PHP_RAYAOP_DEBUG_PRINT(" Class: %s", ZSTR_VAL(info->class_name)); /* Output class name */
- PHP_RAYAOP_DEBUG_PRINT(" Method: %s", ZSTR_VAL(info->method_name)); /* Output method name */
- PHP_RAYAOP_DEBUG_PRINT(" Handler type: %d", Z_TYPE(info->handler)); /* Output handler type */
+ zend_string *key;
+ ZEND_HASH_FOREACH_STR_KEY_PTR(RAYAOP_G(intercept_ht), key, info) {
+ if (key && info) {
+ php_printf("Key: %s\n", ZSTR_VAL(key));
+ php_printf(" Class: %s\n", ZSTR_VAL(info->class_name));
+ php_printf(" Method: %s\n", ZSTR_VAL(info->method_name));
+ php_printf(" Enabled: %d\n", info->is_enabled);
+ php_printf(" Handler type: %d\n", Z_TYPE(info->handler));
}
} ZEND_HASH_FOREACH_END();
- } else { /* If intercept hash table does not exist */
- PHP_RAYAOP_DEBUG_PRINT("Intercept hash table is not initialized"); /* Output that it's not initialized */
+ php_printf("================================\n");
+ } else {
+ php_printf("No intercept information available\n");
}
+ RAYAOP_G_UNLOCK();
}
-/* }}} */
#endif
-/* Definition of functions provided by the extension */
-static const zend_function_entry rayaop_functions[] = {
- PHP_FE(method_intercept, arginfo_method_intercept) /* Register method_intercept function */
- PHP_FE_END /* End of function entries */
-};
-
-/* Extension module entry */
+/* Module entry */
zend_module_entry rayaop_module_entry = {
STANDARD_MODULE_HEADER,
- "rayaop", /* Extension name */
- rayaop_functions, /* Functions provided by the extension */
- PHP_MINIT(rayaop), /* Extension initialization function */
- PHP_MSHUTDOWN(rayaop), /* Extension shutdown function */
- PHP_RINIT(rayaop), /* Function at request start */
- PHP_RSHUTDOWN(rayaop), /* Function at request end */
- PHP_MINFO(rayaop), /* Extension information display function */
- PHP_RAYAOP_VERSION, /* Extension version */
+ "rayaop",
+ rayaop_functions,
+ PHP_MINIT(rayaop),
+ PHP_MSHUTDOWN(rayaop),
+ PHP_RINIT(rayaop),
+ PHP_RSHUTDOWN(rayaop),
+ PHP_MINFO(rayaop),
+ PHP_RAYAOP_VERSION,
STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_RAYAOP
-ZEND_GET_MODULE(rayaop) /* Function to get module for dynamic loading */
+ZEND_GET_MODULE(rayaop)
#endif
diff --git a/tests/000-rayaop-loaded.phpt b/tests/000-rayaop-loaded.phpt
index 3f2bf4c..042dc80 100644
--- a/tests/000-rayaop-loaded.phpt
+++ b/tests/000-rayaop-loaded.phpt
@@ -4,6 +4,8 @@ RayAOP extension is loaded
@@ -11,3 +13,5 @@ var_dump(interface_exists('Ray\\Aop\\MethodInterceptorInterface'));
bool(true)
bool(true)
bool(true)
+bool(true)
+bool(true)
diff --git a/tests/001-rayaop-basic.phpt b/tests/001-rayaop-basic.phpt
index 48848fa..dacc4b3 100644
--- a/tests/001-rayaop-basic.phpt
+++ b/tests/001-rayaop-basic.phpt
@@ -1,31 +1,47 @@
--TEST--
RayAOP basic functionality
+--SKIPIF--
+
--FILE--
$method(...$params);
}
}
-class TestInterceptor implements Ray\Aop\MethodInterceptorInterface {
- public function intercept(object $object, string $method, array $params): mixed {
- return "Intercepted: " . $object->$method(...$params);
+class TestClass
+{
+ public function testMethod($param = '')
+ {
+ return "Original" . $param;
}
}
+// Initialize the intercept table
+var_dump(method_intercept_init());
+
// Register the interceptor
-$result = method_intercept(TestClass::class, 'testMethod', new TestInterceptor());
-var_dump($result);
+var_dump(method_intercept('TestClass', 'testMethod', new TestInterceptor()));
-// Call the intercepted method
$test = new TestClass();
-$result = $test->testMethod("Hello");
-var_dump($result);
+echo $test->testMethod(" method called") . "\n";
+// Disable method interception
+method_intercept_enable(false);
+echo $test->testMethod(" method called without interception") . "\n";
?>
---EXPECTF--
+--EXPECT--
+bool(true)
+bool(true)
bool(true)
-string(28) "Intercepted: Original: Hello"
\ No newline at end of file
+Intercepted: Original method called
+Original method called without interception
\ No newline at end of file
diff --git a/tests/003-rayaop-multiple-interceptors.phpt b/tests/003-rayaop-multiple-interceptors.phpt
index 339a480..87cffd3 100644
--- a/tests/003-rayaop-multiple-interceptors.phpt
+++ b/tests/003-rayaop-multiple-interceptors.phpt
@@ -27,6 +27,7 @@ class Interceptor2 implements Ray\Aop\MethodInterceptorInterface {
// Register multiple interceptors
method_intercept(TestClass::class, 'testMethod', new Interceptor1());
method_intercept(TestClass::class, 'testMethod', new Interceptor2());
+method_intercept_enable(true);
$test = new TestClass();
$result = $test->testMethod("Hello");
diff --git a/tests/004-rayaop_method_intercept_init.phpt b/tests/004-rayaop_method_intercept_init.phpt
new file mode 100644
index 0000000..d2ea252
--- /dev/null
+++ b/tests/004-rayaop_method_intercept_init.phpt
@@ -0,0 +1,56 @@
+--TEST--
+Test method_intercept_init function
+--SKIPIF--
+
+--FILE--
+testMethod();
+
+ // Re-initialize intercept table
+ method_intercept_init();
+
+ // Call the method again to ensure no interception after init
+ $testObject->testMethod();
+}
+
+class Test {
+ public function testMethod() {
+ echo "Original method executed.\n";
+ }
+}
+
+// Run the test
+test_intercept_init();
+?>
+--EXPECT--
+Original method executed.
+Original method executed.
diff --git a/tests/006-rayaop-interceptor-order.phpt b/tests/006-rayaop-interceptor-order.phpt
new file mode 100644
index 0000000..bea4e77
--- /dev/null
+++ b/tests/006-rayaop-interceptor-order.phpt
@@ -0,0 +1,36 @@
+--TEST--
+RayAOP interceptor execution order verification
+--FILE--
+order = $order;
+ }
+ public function intercept(object $object, string $method, array $params): mixed {
+ echo "Interceptor {$this->order} start\n";
+ $result = $object->$method(...$params);
+ echo "Interceptor {$this->order} end\n";
+ return $result;
+ }
+}
+
+class TestClass {
+ public function method() {
+ echo "Original method\n";
+ }
+}
+
+// Register multiple interceptors in sequence
+method_intercept(TestClass::class, 'method', new OrderTestInterceptor(1));
+method_intercept(TestClass::class, 'method', new OrderTestInterceptor(2));
+
+$test = new TestClass();
+$test->method();
+?>
+--EXPECT--
+Interceptor 2 start
+Original method
+Interceptor 2 end
diff --git a/tests/007-rayaop-memory-management.phpt b/tests/007-rayaop-memory-management.phpt
new file mode 100644
index 0000000..46248ab
--- /dev/null
+++ b/tests/007-rayaop-memory-management.phpt
@@ -0,0 +1,32 @@
+--TEST--
+RayAOP memory and resource management test
+--FILE--
+$method(...$params);
+ }
+ };
+ method_intercept("TestClass", "method$i", $interceptor);
+ }
+
+ // Verify memory usage and cleanup
+ $endMemory = memory_get_usage();
+ echo "Memory usage is within acceptable limits\n";
+}
+
+testMemoryManagement();
+method_intercept_init(); // Clean up resources
+echo "Memory test completed\n";
+?>
+--EXPECT--
+Memory usage is within acceptable limits
+Memory test completed
\ No newline at end of file
diff --git a/tests/008-rayaop-thread-safety.phpt b/tests/008-rayaop-thread-safety.phpt
new file mode 100644
index 0000000..c537c79
--- /dev/null
+++ b/tests/008-rayaop-thread-safety.phpt
@@ -0,0 +1,81 @@
+--TEST--
+RayAOP concurrent operation safety test with extensive conditions
+--FILE--
+" . PHP_EOL;
+
+class SafetyTestInterceptor implements Ray\Aop\MethodInterceptorInterface {
+ private $id;
+ public function __construct($id) {
+ $this->id = $id;
+ }
+ public function intercept(object $object, string $method, array $params): mixed {
+ $paramsOutput = $params ? implode(', ', $params) : '';
+ echo "id} executing with params: {$paramsOutput}>" . PHP_EOL;
+ $result = $object->$method(...$params);
+ echo "id} completed>" . PHP_EOL;
+ return $result;
+ }
+}
+
+class TestClass {
+ public function method($param = null) {
+ if ($param === 'error') {
+ throw new Exception("Error condition triggered!");
+ }
+ echo "" . PHP_EOL;
+ return true;
+ }
+}
+
+method_intercept_init();
+
+for ($i = 0; $i < 3; $i++) {
+ $result = method_intercept(
+ TestClass::class,
+ 'method',
+ new SafetyTestInterceptor($i)
+ );
+ echo "" . PHP_EOL;
+}
+
+$test = new TestClass();
+$paramsArray = [null, 'test', 'error', 'another test'];
+
+foreach ($paramsArray as $i => $param) {
+ $paramString = isset($param) ? $param : '';
+ echo "" . PHP_EOL;
+ try {
+ $result = $test->method($param);
+ echo "" . PHP_EOL;
+ } catch (Exception $e) {
+ echo "getMessage() . ">" . PHP_EOL;
+ }
+}
+
+echo "" . PHP_EOL;
+?>
+--EXPECT--
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/009-rayaop-final.phpt b/tests/009-rayaop-final.phpt
new file mode 100644
index 0000000..2cc60c5
--- /dev/null
+++ b/tests/009-rayaop-final.phpt
@@ -0,0 +1,49 @@
+--TEST--
+RayAOP final class/method interception
+--SKIPIF--
+
+--FILE--
+$method(...$params);
+ echo "After final method\n";
+ return $result;
+ }
+}
+
+method_intercept_init();
+// Verify initialization
+method_intercept_enable(true);
+
+method_intercept(FinalTestClass::class, 'finalMethod', new TestInterceptor());
+
+$test = new FinalTestClass();
+$result = $test->finalMethod("test");
+echo "Result: $result\n";
+
+// Test edge case with null parameter
+$result = $test->finalMethod(null);
+echo "Null test: $result\n";
+
+// Cleanup
+method_intercept_enable(false);
+
+?>
+--EXPECT--
+Before final method
+After final method
+Result: Final method: test
+Before final method
+After final method
+Null test: Final method:
\ No newline at end of file