Skip to content

Commit

Permalink
Merge pull request #9 from ray-di/hotfix
Browse files Browse the repository at this point in the history
Bug Fixes: Refined logic for managing execution depth and interception state
  • Loading branch information
koriym authored Dec 9, 2024
2 parents 5fba231 + cf5ffbf commit 969d4ec
Show file tree
Hide file tree
Showing 20 changed files with 832 additions and 393 deletions.
53 changes: 34 additions & 19 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:

strategy:
matrix:
php-version: ['8.1', '8.2', '8.3']
php-version: ['8.1', '8.2', '8.3', '8.4']

steps:
- name: Checkout code
Expand Down Expand Up @@ -50,8 +50,17 @@ jobs:
timeout 60s php -n -dextension=./modules/rayaop.so -dmemory_limit=128M -dreport_memleaks=1 -dzend.assertions=1 -dassert.exception=1 smoke.php
continue-on-error: true

# Add demo directory test
- name: Test demo directory
id: test_demo_dir
run: |
cd demo
composer install
php -dextension=../modules/rayaop.so aop.php
continue-on-error: true

- name: Run Valgrind memory check
if: steps.run_tests.outcome == 'failure' || steps.run_demo.outcome == 'failure'
if: steps.run_tests.outcome == 'failure' || steps.run_demo.outcome == 'failure' || steps.test_demo_dir.outcome == 'failure'
run: |
cat << EOF > valgrind.supp
{
Expand All @@ -65,27 +74,32 @@ jobs:
EOF
valgrind --suppressions=valgrind.supp --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose --log-file=valgrind-out.txt php -n -dextension=./modules/rayaop.so smoke.php
# Also run Valgrind on demo directory
cd demo
valgrind --suppressions=../valgrind.supp --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose --log-file=valgrind-demo-out.txt php -n -dextension=../modules/rayaop.so aop.php
- name: Check Valgrind results
if: steps.run_tests.outcome == 'failure' || steps.run_demo.outcome == 'failure'
if: steps.run_tests.outcome == 'failure' || steps.run_demo.outcome == 'failure' || steps.test_demo_dir.outcome == 'failure'
run: |
if [ -f valgrind-out.txt ]; then
echo "Valgrind log found:"
cat valgrind-out.txt
if ! grep -q "ERROR SUMMARY: 0 errors from 0 contexts" valgrind-out.txt; then
echo "Valgrind found errors"
exit 1
for log in valgrind-out.txt demo/valgrind-demo-out.txt; do
if [ -f "$log" ]; then
echo "Checking Valgrind log: $log"
cat "$log"
if ! grep -q "ERROR SUMMARY: 0 errors from 0 contexts" "$log"; then
echo "Valgrind found errors in $log"
exit 1
fi
fi
else
echo "Valgrind log not found. This is unexpected."
exit 1
fi
done
- name: Upload Valgrind log file
if: (steps.run_tests.outcome == 'failure' || steps.run_demo.outcome == 'failure') && always()
- name: Upload Valgrind logs
if: (steps.run_tests.outcome == 'failure' || steps.run_demo.outcome == 'failure' || steps.test_demo_dir.outcome == 'failure') && always()
uses: actions/upload-artifact@v4
with:
name: valgrind-log
path: valgrind-out.txt
name: valgrind-logs
path: |
valgrind-out.txt
demo/valgrind-demo-out.txt
if-no-files-found: warn

- name: Upload test logs
Expand All @@ -96,12 +110,13 @@ jobs:
path: |
tests/*.log
tests/*.sh
demo/*.log
if-no-files-found: warn

- name: Final status check
if: always()
run: |
if [ "${{ steps.run_tests.outcome }}" == "failure" ] || [ "${{ steps.run_demo.outcome }}" == "failure" ]; then
echo "Tests or demo run failed. Please check the logs for more information."
if [ "${{ steps.run_tests.outcome }}" == "failure" ] || [ "${{ steps.run_demo.outcome }}" == "failure" ] || [ "${{ steps.test_demo_dir.outcome }}" == "failure" ]; then
echo "Tests, demo run, or demo directory test failed. Please check the logs for more information."
exit 1
fi
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,6 @@
/Makefile.objects
/cmake-build-debug
/modules/rayaop.so

/demo/vendor/
/demo/tmp/
212 changes: 164 additions & 48 deletions README.ja.md
Original file line number Diff line number Diff line change
@@ -1,134 +1,250 @@
# Ray.Aop PHP拡張
# Ray.Aop PHP拡張機能

[![Build and Test PHP Extension](https://github.com/ray-di/ext-rayaop/actions/workflows/build.yml/badge.svg)](https://github.com/ray-di/ext-rayaop/actions/workflows/build.yml)

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

[Ray.Aop](https://github.com/ray-di/Ray.Aop)のためのコアメソッドインターセプション機能を提供する低レベルのPHP拡張です。この拡張は単体での使用も可能ですが、Ray.Aopのより高度なAOP機能の基盤として設計されています。
[Ray.Aop](https://github.com/ray-di/Ray.Aop)のコアとなるメソッドインターセプション機能を提供する低レベルPHP拡張機能です。この拡張機能は単独でも使用できますが、Ray.Aopのより高度なAOP機能の基盤として設計されています。

## 特徴

- 効率的な低レベルメソッドインターセプション
- finalクラスおよびメソッドのインターセプトをサポート
- 引数および戻り値の完全な変更サポート
- `new`キーワードとのシームレスな互換性
- スレッドセーフな操作サポート
- finalクラスやメソッドのインターセプトに対応
- パラメータと戻り値の完全な変更サポート
- `new`キーワードとのシームレスな連携
- スレッドセーフな動作のサポート
- 包括的なエラーハンドリングとデバッグ機能
- 適切なリソース管理による効率的なメモリ使用

## 要件

- PHP 8.1以上
- Linux、macOS、またはWindowsでの適切なビルドツール
- マルチスレッド環境ではスレッドセーフなPHPビルドを推奨
- Linux、macOS、またはWindows(適切なビルドツールが必要)
- マルチスレッド環境では、スレッドセーフビルドのPHPを推奨

## インストール

1. リポジトリをクローンします:
1. リポジトリのクローン:
```bash
git clone https://github.com/ray-di/ext-rayaop.git
cd ext-rayaop
```

2. 拡張をビルドし、インストールします:
2. ビルドとインストール:
```bash
phpize
./configure
make
make install
```

3. php.iniファイルに次の行を追加します:
3. php.iniに以下の行を追加:
```ini
extension=rayaop.so # Unix/Linuxの場合
extension=rayaop.dll # Windowsの場合
extension=rayaop.so # Unix/Linux用
extension=rayaop.dll # Windows用

; オプション: デバッグモードの有効化
; rayaop.debug_level = 1
```

4. インストールを確認します:
4. インストールの確認:
```bash
php -m | grep rayaop
```

## API リファレンス

### コア関数

#### method_intercept_init()
インターセプションシステムを初期化します。インターセプターの登録前に必ず呼び出す必要があります。

```php
bool method_intercept_init()
```

成功時は`true`、失敗時は`false`を返します。

#### method_intercept()
特定のクラスメソッドにインターセプターを登録します。

```php
bool method_intercept(string $class_name, string $method_name, Ray\Aop\MethodInterceptorInterface $interceptor)
```

- すでにインターセプターが登録されている場合は上書きされます
- 成功時は`true`、失敗時は`false`を返します

#### method_intercept_enable()
メソッドインターセプションをグローバルに有効/無効にします。

```php
void method_intercept_enable(bool $enable)
```

### スレッドセーフティ

この拡張機能は完全にスレッドセーフで、以下の機構を使用しています:

- ミューテックスによるリソース保護
- グローバル状態用のスレッドローカルストレージ
- マルチスレッド環境での安全な初期化とクリーンアップ

マルチスレッド環境(PHP-FPMなど)での使用時の注意点:
- PHPでZTS(Zend Thread Safety)が有効になっていることを確認
- 各スレッドが独自のインターセプション状態を維持
- リソースのクリーンアップは自動的にスレッドごとに処理

## エラーハンドリング

### エラーコード

拡張機能は以下のエラーコードを定義しています:

- `RAYAOP_E_MEMORY_ALLOCATION (1)`: メモリ割り当て失敗
- `RAYAOP_E_HASH_UPDATE (2)`: ハッシュテーブル更新失敗
- `RAYAOP_E_INVALID_HANDLER (3)`: 無効なインターセプターハンドラー
- `RAYAOP_E_MAX_DEPTH_EXCEEDED (4)`: 最大インターセプション深度超過
- `RAYAOP_E_NULL_POINTER (5)`: NULLポインターエラー
- `RAYAOP_E_INVALID_STATE (6)`: 無効な内部状態

エラーはPHPのエラー報告システムを通じて報告されます。これらのエラーをキャッチして処理するには、エラー報告を有効にしてください。

## デバッグモード

php.iniでデバッグレベルを設定することでデバッグモードを有効にできます:

```ini
rayaop.debug_level = 1
```

デバッグ出力には以下が含まれます:
- インターセプター登録イベント
- メソッドインターセプションのトレース
- リソースの割り当て/解放
- エラー条件とスタックトレース

## パフォーマンスに関する考慮事項

### 実行深度

無限再帰を防ぐため、最大インターセプション深度に制限があります:
```php
#define MAX_EXECUTION_DEPTH 100
```

ネストされたインターセプターを設計する際は、この制限を考慮してください。

### メモリ管理

拡張機能は慎重なメモリ管理を実装しています:
- インターセプターリソースの自動クリーンアップ
- PHPオブジェクトの適切な参照カウント
- インターセプター置換時の即時リソース解放
- リクエスト終了時のクリーンアップ

### パフォーマンスへの影響

メソッドインターセプションによるオーバーヘッド:
- 直接のメソッド呼び出し:約0.1-0.2μsの追加オーバーヘッド
- 単純なインターセプターでの呼び出し:約1-2μsのオーバーヘッド
- 複雑なインターセプターは実装に応じてより多くのオーバーヘッドが発生する可能性があります

パフォーマンスに関するヒント:
- インターセプターはアプリケーション初期化時に登録
- インターセプターの頻繁な登録/解除を避ける
- パフォーマンスが重要なコードには単純なインターセプターを使用
- 必要のない場合はインターセプションを無効化

## 設計の決定

この拡張は、最小限で高パフォーマンスなメソッドインターセプション機能を提供します。
この拡張機能は、最小限の高性能メソッドインターセプション機能を提供します:

- メソッドごとに1つのインターセプター:各メソッドに対して最後に登録されたインターセプターが優先されます
- finalクラスサポート:純粋なPHP実装と異なり、finalクラスおよびメソッドをインターセプト可能
- 素のインターセプション:組み込みのマッチングや条件はありません(これらの機能はRay.Aopで提供されます)
- スレッドセーフ:PHP-FPMなどのマルチスレッド環境でも安全に使用可能
- メソッドあたり1つのインターセプター:最後に登録されたインターセプターが優先されます
- 新しいインターセプターを登録すると、以前のものは自動的に解除されます
- この設計により、予測可能な動作と最適なパフォーマンスを確保
- finalクラスのサポート:Pure PHP実装では不可能なfinalクラスやメソッドのインターセプトが可能
- 生のインターセプション:組み込みのマッチングや条件はなし(これらの機能にはRay.Aopを使用)
- スレッドセーフ:PHP-FPMなどのマルチスレッド環境で安全に使用可能

## Ray.Aopとの関係

この拡張は低レベルのメソッドインターセプションを提供し[Ray.Aop](https://github.com/ray-di/Ray.Aop)は高レベルのAOP機能を提供します
この拡張機能は低レベルのメソッドインターセプションを提供し[Ray.Aop](https://github.com/ray-di/Ray.Aop)は高レベルのAOP機能を提供します

Ray.Aopが提供する機能
- マッチャーを用いた条件付きインターセプション
- メソッドごとの複数インターセプター
Ray.Aopの提供する機能
- Matchersを使用した条件付きインターセプション
- メソッドあたり複数のインターセプター
- 属性/アノテーションベースのインターセプション
- 高度なAOP機能

両方を併用する場合
両者を併用する場合
- Ray.Aopが高レベルのAOPロジックを処理
- この拡張が低レベルのインターセプション機構を提供
- Ray.Aopは、パフォーマンス向上のためにこの拡張を自動的に利用
- この拡張機能が低レベルのインターセプション機構を提供
- Ray.Aopは可能な場合、パフォーマンス向上のためにこの拡張機能を自動的に利用

## 基本的な使い方
## 基本的な使用方法

### シンプルなインターセプター
```php
class LoggingInterceptor implements Ray\Aop\MethodInterceptorInterface
{
public function intercept(object $object, string $method, array $params): mixed
{
echo "Before {$method}\n";
echo "実行前 {$method}\n";
$result = $object->$method(...$params);
echo "After {$method}\n";
echo "実行後 {$method}\n";
return $result;
}
}

// インターセプターを登録
method_intercept(TestClass::class, 'testMethod', new LoggingInterceptor());
```

### メソッドインターセプションの設定
```php
// インターセプションシステムを初期化
// 初期化とインターセプションの有効化
method_intercept_init();

// メソッドインターセプションを有効化
method_intercept_enable(true);

// インターセプターを登録
method_intercept(MyClass::class, 'myMethod', new MyInterceptor());
// インターセプターの登録
method_intercept(TestClass::class, 'testMethod', new LoggingInterceptor());
```

## 開発

### ビルドスクリプト
```bash
./build.sh clean # ビルド環境をクリーン
./build.sh prepare # ビルド環境を準備
./build.sh build # 拡張をビルド
./build.sh run # 拡張を実行
./build.sh all # 全てのステップを実行
./build.sh clean # ビルド環境のクリーン
./build.sh prepare # ビルド環境の準備
./build.sh build # 拡張機能のビルド
./build.sh run # 拡張機能の実行
./build.sh all # すべての手順を実行
```

### テスト
```bash
make test
```

特定のテストを実行する場合:
特定のテストの実行:
```bash
make test TESTS="-v tests/your_specific_test.phpt"
```

## 既知の制限事項

1. メソッドあたり1つのインターセプターのみ
2. 実行深度の最大値は100レベル
3. メソッドインターセプションのパターンマッチング機能なし
4. インターセプターはメソッドごとに個別に登録が必要

## ライセンス

[MIT License](LICENSE)
[MITライセンス](LICENSE)

## 作者

Akihito Koriyama

この拡張は、AIペアプログラミングの支援を受けて開発され、PHP拡張開発およびPECL基準に関する複雑さを克服するのに役立ちました。
この拡張機能は、PHP拡張機能開発とPECL標準の複雑さをナビゲートするのに役立つAIペアプログラミングの支援を受けて開発されました。

## 謝辞

このプロジェクトは[JetBrains CLion](https://www.jetbrains.com/clion/)を使用して作成されました。CLionは[オープンソースライセンス](https://www.jetbrains.com/community/opensource/)で無料で利用できます。

強力で使いやすい開発環境を提供してくださったJetBrainsに感謝いたします。
Loading

0 comments on commit 969d4ec

Please sign in to comment.