Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce method_intercept_init(), method_intercept_enable() function #7

Merged
merged 43 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
761d789
Add method_intercept_init function to Ray AOP extension
koriym Jul 11, 2024
dc779fb
Add test for method_intercept_init function
koriym Jul 11, 2024
f40bf73
Enable debug mode and add detailed debug printing in rayaop
koriym Jul 11, 2024
d7347b6
Add execution depth tracking to rayaop module
koriym Jul 11, 2024
f093f39
Add method interception enabling functionality
koriym Jul 11, 2024
2009596
Add null check for execute_data->return_value
koriym Jul 11, 2024
564364f
Enhance debug logging and refactor execution interception
koriym Jul 11, 2024
d4a80e3
Enable method interception in tests
koriym Jul 11, 2024
ebad93f
Add new functions check to rayaop-loaded test
koriym Jul 11, 2024
7ba055e
Update build workflow to include a custom php.ini
koriym Jul 11, 2024
55b778c
Disable RAYAOP_DEBUG in rayaop.c
koriym Jul 11, 2024
ceff605
Add AddressSanitizer flags and paths to config.m4
koriym Jul 18, 2024
f7525e7
Update AddressSanitizer flags and paths in config.m4
koriym Jul 18, 2024
0b14f8a
Refactor PHP extension: enhance structure and add features
koriym Nov 4, 2024
6acdf83
Update artifact upload action to v4
koriym Nov 4, 2024
00d5db4
Add method interception and improve error handling
koriym Nov 4, 2024
bb4149b
Remove method interception disabled check
koriym Nov 5, 2024
9cc85bb
Add test for RayAOP maximum recursion and error handling
koriym Nov 5, 2024
2794d36
Add test for verifying RayAOP interceptor execution order
koriym Nov 5, 2024
db35726
Add memory management test for RayAOP
koriym Nov 5, 2024
ac07b80
Add thread safety test for RayAOP interceptors
koriym Nov 5, 2024
44e753b
Update .gitignore to exclude additional files
koriym Nov 5, 2024
895f5fa
Add test for RayAOP final class/method interception
koriym Nov 5, 2024
210d10d
Update README.md for improved clarity and completeness
koriym Nov 5, 2024
7522faa
Fix request initialization and ensure correct handler setup
koriym Nov 5, 2024
8935535
Remove test for RayAOP maximum recursion and error handling
koriym Nov 5, 2024
7df9c8f
Update tests/009-rayaop-final.phpt
koriym Nov 5, 2024
20a79ac
Refactor function name for consistency
koriym Nov 5, 2024
6e38539
Change _IS_BOOL to IS_BOOL
koriym Nov 5, 2024
2192bb4
Update checkout action to v4 in build workflow
koriym Nov 5, 2024
9e4e3cc
Revert "Change _IS_BOOL to IS_BOOL"
koriym Nov 5, 2024
6065701
fixup! Update tests/009-rayaop-final.phpt
koriym Nov 5, 2024
30a511a
Refactor error handling in intercept hash update function
koriym Nov 5, 2024
eaa2170
Refactor interception hash table update error handling
koriym Nov 5, 2024
54c0069
Add cleanup for intercept params on failure
koriym Nov 5, 2024
32a41a2
Fix memory leak in cleanup_intercept_params function
koriym Nov 5, 2024
00f3fe4
Revert "Fix memory leak in cleanup_intercept_params function"
koriym Nov 5, 2024
088caa7
Update test for RayAOP thread safety with error handling
koriym Nov 5, 2024
998d9f6
Enhance README.md with detailed features, security, and usage instruc…
koriym Nov 5, 2024
e649886
Clarify method interception behavior in README
koriym Nov 5, 2024
aa024ab
Add thread safety in ZTS mode for RayAOP
koriym Nov 5, 2024
1837515
Refactor and simplify README.md
koriym Nov 5, 2024
f50ea2f
Add Acknowledgments section to README
koriym Nov 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -78,15 +82,15 @@ 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
if-no-files-found: warn
koriym marked this conversation as resolved.
Show resolved Hide resolved

- name: Upload test logs
if: failure()
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: test-logs
path: |
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@
# Ignore directories and its content
/Makefile.fragments
/Makefile.objects
/cmake-build-debug
/cmake-build-debug
/modules/rayaop.so
180 changes: 76 additions & 104 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,165 +4,137 @@

<img src="https://ray-di.github.io/images/logo.svg" alt="ray-di logo" width="150px;">

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.
30 changes: 6 additions & 24 deletions config.m4
Original file line number Diff line number Diff line change
@@ -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
Loading