Skip to content

Commit 83594c9

Browse files
author
tourze-ci
committed
chore: 将 enum 目录下的模块统一移动到 packages 目录
1 parent ee27857 commit 83594c9

24 files changed

+1520
-0
lines changed

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 tourze
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# AopBundle
2+
3+
[![Latest Version](https://img.shields.io/packagist/v/tourze/symfony-aop-bundle.svg?style=flat-square)](https://packagist.org/packages/tourze/symfony-aop-bundle)
4+
5+
[English](README.md) | [中文](README.zh-CN.md)
6+
7+
## Overview
8+
9+
AopBundle is a Symfony bundle that implements Aspect-Oriented Programming (AOP) using PHP 8's Attribute features. It enables adding cross-cutting concerns (like logging, caching, transactions) to your code without modifying the core logic.
10+
11+
## Requirements
12+
13+
- PHP 8.1+
14+
- Symfony 6.4+
15+
16+
## Installation
17+
18+
```bash
19+
composer require tourze/symfony-aop-bundle
20+
```
21+
22+
## Features
23+
24+
- Define aspects using PHP 8 attributes
25+
- Support for multiple advice types (Before, After, AfterReturning, AfterThrowing)
26+
- Join point context with access to method parameters and return values
27+
- Powerful pointcut expressions for targeting specific services/methods
28+
- Built-in stopwatch support for performance monitoring
29+
- Exception handling capabilities
30+
31+
## Basic Usage
32+
33+
### 1. Define an Aspect
34+
35+
Create a class with the `#[Aspect]` attribute and define advice methods:
36+
37+
```php
38+
<?php
39+
40+
namespace App\Aspect;
41+
42+
use Tourze\Symfony\Aop\Attribute\Aspect;
43+
use Tourze\Symfony\Aop\Attribute\Before;
44+
use Tourze\Symfony\Aop\Model\JoinPoint;
45+
46+
#[Aspect]
47+
class LoggingAspect
48+
{
49+
#[Before('class.getName() === "App\\Service\\UserService" && method.getName() === "createUser"')]
50+
public function logBefore(JoinPoint $joinPoint): void
51+
{
52+
// Logging logic before method execution
53+
}
54+
}
55+
```
56+
57+
### 2. Available Advice Types
58+
59+
- `#[Before]` - Executed before the target method
60+
- `#[After]` - Executed after the target method (regardless of exceptions)
61+
- `#[AfterReturning]` - Executed after successful method return
62+
- `#[AfterThrowing]` - Executed when the target method throws an exception
63+
- `#[CatchException]` - Used for exception handling
64+
65+
### 3. JoinPoint
66+
67+
The `JoinPoint` object provides context for the intercepted method:
68+
69+
```php
70+
$joinPoint->getInstance(); // The service instance
71+
$joinPoint->getMethod(); // Method name being executed
72+
$joinPoint->getParams(); // Method parameters
73+
$joinPoint->getReturnValue(); // Return value (for after advice)
74+
$joinPoint->getException(); // Exception (for exception advice)
75+
$joinPoint->proceed(); // Manually execute the original method
76+
```
77+
78+
### 4. Pointcut Expressions
79+
80+
Define where advice should be applied:
81+
82+
```php
83+
// Match by service ID
84+
#[Before(serviceIds: ['app.user_service'])]
85+
86+
// Match by class attribute
87+
#[AfterThrowing(classAttribute: CatchException::class)]
88+
89+
// Match by method attribute
90+
#[After(methodAttribute: SomeAttribute::class)]
91+
92+
// Match by service tags
93+
#[Before(serviceTags: ['app.loggable'])]
94+
95+
// Match by parent class
96+
#[Before(parentClasses: [BaseRepository::class])]
97+
98+
// Custom expression
99+
#[Before('class.getName() === "App\\Service\\UserService"')]
100+
```
101+
102+
### 5. Performance Monitoring
103+
104+
```php
105+
use Tourze\Symfony\Aop\Attribute\Stopwatch;
106+
107+
class UserService
108+
{
109+
#[Stopwatch]
110+
public function complexOperation()
111+
{
112+
// Time-consuming operation
113+
}
114+
}
115+
```
116+
117+
## Best Practices
118+
119+
1. **Keep aspects focused**
120+
- Each aspect should have a single responsibility
121+
- Avoid heavy operations in advice methods
122+
123+
2. **Advice execution order**
124+
- Before advice is executed in declaration order
125+
- After/AfterReturning/AfterThrowing advice is executed in reverse order
126+
127+
3. **Performance considerations**
128+
- Use AOP only when necessary
129+
- Consider the performance impact in production
130+
- Use Stopwatch to monitor method execution time
131+
132+
4. **Exception handling**
133+
- Be careful with exception handling in advice
134+
- Consider using AfterThrowing for exception logging
135+
- Use CatchException for controlled exception handling
136+
137+
## Configuration
138+
139+
No additional configuration is required. The bundle automatically detects services with the `#[Aspect]` attribute and applies advice to matching services.
140+
141+
## Contributing
142+
143+
Contributions are welcome. Please feel free to submit a Pull Request.
144+
145+
## License
146+
147+
This bundle is available under the MIT license. See the [LICENSE](LICENSE) file for more information.

README.zh-CN.md

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# AopBundle
2+
3+
[![Latest Version](https://img.shields.io/packagist/v/tourze/symfony-aop-bundle.svg?style=flat-square)](https://packagist.org/packages/tourze/symfony-aop-bundle)
4+
5+
[English](README.md) | [中文](README.zh-CN.md)
6+
7+
## 概述
8+
9+
AopBundle 是一个基于 Symfony 的 AOP (面向切面编程) 实现包。它通过 PHP 8 的 Attribute 特性提供了声明式的切面编程能力,可以在不修改原有代码的情况下为方法添加横切关注点(如日志、缓存、事务等)。
10+
11+
## 系统要求
12+
13+
- PHP 8.1+
14+
- Symfony 6.4+
15+
16+
## 安装方法
17+
18+
```bash
19+
composer require tourze/symfony-aop-bundle
20+
```
21+
22+
## 主要功能
23+
24+
- 使用 PHP 8 属性定义切面
25+
- 支持多种通知类型(Before、After、AfterReturning、AfterThrowing)
26+
- 通过 JoinPoint 获取方法参数和返回值
27+
- 强大的切点表达式,用于定位特定服务/方法
28+
- 内置 Stopwatch 支持,用于性能监控
29+
- 异常处理能力
30+
31+
## 基本使用
32+
33+
### 1. 定义切面
34+
35+
创建一个带有 `#[Aspect]` 属性的类,并定义通知方法:
36+
37+
```php
38+
<?php
39+
40+
namespace App\Aspect;
41+
42+
use Tourze\Symfony\Aop\Attribute\Aspect;
43+
use Tourze\Symfony\Aop\Attribute\Before;
44+
use Tourze\Symfony\Aop\Model\JoinPoint;
45+
46+
#[Aspect]
47+
class LoggingAspect
48+
{
49+
#[Before('class.getName() === "App\\Service\\UserService" && method.getName() === "createUser"')]
50+
public function logBefore(JoinPoint $joinPoint): void
51+
{
52+
// 方法执行前的日志逻辑
53+
}
54+
}
55+
```
56+
57+
### 2. 可用的通知类型
58+
59+
- `#[Before]` - 在目标方法执行前执行
60+
- `#[After]` - 在目标方法执行后执行(无论是否抛出异常)
61+
- `#[AfterReturning]` - 在目标方法成功返回后执行
62+
- `#[AfterThrowing]` - 在目标方法抛出异常时执行
63+
- `#[CatchException]` - 用于异常处理
64+
65+
### 3. JoinPoint
66+
67+
`JoinPoint` 对象提供了被拦截方法的上下文:
68+
69+
```php
70+
$joinPoint->getInstance(); // 服务实例
71+
$joinPoint->getMethod(); // 正在执行的方法名
72+
$joinPoint->getParams(); // 方法参数
73+
$joinPoint->getReturnValue(); // 返回值(用于 After 通知)
74+
$joinPoint->getException(); // 异常(用于异常通知)
75+
$joinPoint->proceed(); // 手动执行原始方法
76+
```
77+
78+
### 4. 切点表达式
79+
80+
定义通知应该应用的位置:
81+
82+
```php
83+
// 通过服务 ID 匹配
84+
#[Before(serviceIds: ['app.user_service'])]
85+
86+
// 通过类属性匹配
87+
#[AfterThrowing(classAttribute: CatchException::class)]
88+
89+
// 通过方法属性匹配
90+
#[After(methodAttribute: SomeAttribute::class)]
91+
92+
// 通过服务标签匹配
93+
#[Before(serviceTags: ['app.loggable'])]
94+
95+
// 通过父类匹配
96+
#[Before(parentClasses: [BaseRepository::class])]
97+
98+
// 自定义表达式
99+
#[Before('class.getName() === "App\\Service\\UserService"')]
100+
```
101+
102+
### 5. 性能监控
103+
104+
```php
105+
use Tourze\Symfony\Aop\Attribute\Stopwatch;
106+
107+
class UserService
108+
{
109+
#[Stopwatch]
110+
public function complexOperation()
111+
{
112+
// 耗时操作
113+
}
114+
}
115+
```
116+
117+
## 最佳实践
118+
119+
1. **保持切面专注**
120+
- 每个切面应该只有一个责任
121+
- 避免在通知方法中执行重量级操作
122+
123+
2. **通知执行顺序**
124+
- Before 通知按声明顺序执行
125+
- After/AfterReturning/AfterThrowing 通知按声明的逆序执行
126+
127+
3. **性能考虑**
128+
- 只在必要时使用 AOP
129+
- 考虑在生产环境中的性能影响
130+
- 使用 Stopwatch 监控方法执行时间
131+
132+
4. **异常处理**
133+
- 在通知中小心处理异常
134+
- 考虑使用 AfterThrowing 进行异常日志记录
135+
- 使用 CatchException 进行受控异常处理
136+
137+
## 配置
138+
139+
不需要额外配置。该包会自动检测带有 `#[Aspect]` 属性的服务,并将通知应用于匹配的服务。
140+
141+
## 贡献
142+
143+
欢迎贡献。请随时提交 Pull Request。
144+
145+
## 许可证
146+
147+
此包在 MIT 许可下可用。有关更多信息,请参阅 [LICENSE](LICENSE) 文件。

composer.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "tourze/symfony-aop-bundle",
3+
"description": "AOP for Symfony Framework",
4+
"license": "MIT",
5+
"require": {
6+
"php": "^8.1",
7+
"friendsofphp/proxy-manager-lts": "^1.0.18",
8+
"symfony/console": "^6.4",
9+
"symfony/expression-language": "^6.4",
10+
"symfony/framework-bundle": "^6.4",
11+
"symfony/stopwatch": "^6.4",
12+
"symfony/yaml": "^6.4 || ^7.1",
13+
"tourze/arrayable": "0.0.*"
14+
},
15+
"autoload": {
16+
"psr-4": {
17+
"Tourze\\Symfony\\Aop\\": "src"
18+
}
19+
}
20+
}

src/AopBundle.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace Tourze\Symfony\Aop;
4+
5+
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
6+
use Symfony\Component\DependencyInjection\ContainerBuilder;
7+
use Symfony\Component\HttpKernel\Bundle\Bundle;
8+
use Tourze\Symfony\Aop\DependencyInjection\Compiler\AopAttributeCompilerPass;
9+
10+
class AopBundle extends Bundle
11+
{
12+
public function build(ContainerBuilder $container): void
13+
{
14+
parent::build($container);
15+
16+
$container->addCompilerPass(new AopAttributeCompilerPass(), PassConfig::TYPE_BEFORE_REMOVING);
17+
}
18+
}

src/Aspect/ExceptionAspect.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace Tourze\Symfony\Aop\Aspect;
4+
5+
use Tourze\Symfony\Aop\Attribute\AfterThrowing;
6+
use Tourze\Symfony\Aop\Attribute\Aspect;
7+
use Tourze\Symfony\Aop\Attribute\CatchException;
8+
use Tourze\Symfony\Aop\Model\JoinPoint;
9+
10+
#[Aspect]
11+
class ExceptionAspect
12+
{
13+
#[AfterThrowing(classAttribute: CatchException::class)]
14+
public function catchException(JoinPoint $joinPoint): void
15+
{
16+
if ($joinPoint->getException()) {
17+
//dump($joinPoint->getException());
18+
}
19+
}
20+
}

0 commit comments

Comments
 (0)