Skip to content

Commit e79b65f

Browse files
Copilothuangdijia
andcommitted
Add documentation for co-phpunit and command-benchmark components
Co-authored-by: huangdijia <8337659+huangdijia@users.noreply.github.com>
1 parent 7da7ddb commit e79b65f

12 files changed

Lines changed: 1308 additions & 0 deletions

File tree

docs/.vitepress/src/en/sidebars.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ const sidebar:DefaultTheme.Sidebar = {
1313
text: 'Cache',
1414
link: '/en/components/cache.md'
1515
},
16+
{
17+
text: 'Co-PHPUnit',
18+
link: '/en/components/co-phpunit.md'
19+
},
20+
{
21+
text: 'Command Benchmark',
22+
link: '/en/components/command-benchmark.md'
23+
},
1624
{
1725
text: 'Command Signals',
1826
link: '/en/components/command-signals.md'

docs/.vitepress/src/zh-cn/sidebars.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ const sidebar:DefaultTheme.Sidebar = {
2626
text: 'Cache',
2727
link: '/zh-cn/components/cache.md'
2828
},
29+
{
30+
text: 'Co-PHPUnit',
31+
link: '/zh-cn/components/co-phpunit.md'
32+
},
33+
{
34+
text: 'Command Benchmark',
35+
link: '/zh-cn/components/command-benchmark.md'
36+
},
2937
{
3038
text: 'Command Signals',
3139
link: '/zh-cn/components/command-signals.md'

docs/.vitepress/src/zh-hk/sidebars.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ const sidebar:DefaultTheme.Sidebar = {
2626
text: 'Cache',
2727
link: '/zh-hk/components/cache.md'
2828
},
29+
{
30+
text: 'Co-PHPUnit',
31+
link: '/zh-hk/components/co-phpunit.md'
32+
},
33+
{
34+
text: 'Command Benchmark',
35+
link: '/zh-hk/components/command-benchmark.md'
36+
},
2937
{
3038
text: 'Command Signals',
3139
link: '/zh-hk/components/command-signals.md'

docs/.vitepress/src/zh-tw/sidebars.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ const sidebar:DefaultTheme.Sidebar = {
2626
text: 'Cache',
2727
link: '/zh-tw/components/cache.md'
2828
},
29+
{
30+
text: 'Co-PHPUnit',
31+
link: '/zh-tw/components/co-phpunit.md'
32+
},
33+
{
34+
text: 'Command Benchmark',
35+
link: '/zh-tw/components/command-benchmark.md'
36+
},
2937
{
3038
text: 'Command Signals',
3139
link: '/zh-tw/components/command-signals.md'

docs/en/components/co-phpunit.md

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
# Co-PHPUnit
2+
3+
## Introduction
4+
5+
`friendsofhyperf/co-phpunit` is a PHPUnit extension specifically designed for testing Hyperf applications and other Swoole-based frameworks, enabling tests to run inside Swoole coroutines.
6+
7+
## Installation
8+
9+
```bash
10+
composer require friendsofhyperf/co-phpunit --dev
11+
```
12+
13+
## Why Co-PHPUnit?
14+
15+
When testing Hyperf applications, many components rely on Swoole's coroutine context to function properly. Running tests in a traditional synchronous environment can lead to issues such as:
16+
17+
- Coroutine context not being available
18+
- Timers and event loops not working correctly
19+
- Coordinator pattern failures
20+
- Database connection pool issues
21+
22+
Co-PHPUnit solves these problems by automatically wrapping test execution in a Swoole coroutine context when needed.
23+
24+
## Usage
25+
26+
### Basic Usage
27+
28+
Simply use the `RunTestsInCoroutine` trait in your test class:
29+
30+
```php
31+
<?php
32+
33+
namespace Your\Namespace\Tests;
34+
35+
use FriendsOfHyperf\CoPHPUnit\Concerns\RunTestsInCoroutine;
36+
use PHPUnit\Framework\TestCase;
37+
38+
class YourTest extends TestCase
39+
{
40+
use RunTestsInCoroutine;
41+
42+
public function testSomething()
43+
{
44+
// Your test code here
45+
// This will automatically run inside a Swoole coroutine
46+
}
47+
}
48+
```
49+
50+
### Disabling Coroutine for Specific Tests
51+
52+
If you need to disable coroutine execution for a specific test class, set the `$enableCoroutine` property to `false`:
53+
54+
```php
55+
<?php
56+
57+
namespace Your\Namespace\Tests;
58+
59+
use FriendsOfHyperf\CoPHPUnit\Concerns\RunTestsInCoroutine;
60+
use PHPUnit\Framework\TestCase;
61+
62+
class YourTest extends TestCase
63+
{
64+
use RunTestsInCoroutine;
65+
66+
protected bool $enableCoroutine = false;
67+
68+
public function testSomething()
69+
{
70+
// This test will run in normal synchronous mode
71+
}
72+
}
73+
```
74+
75+
## How It Works
76+
77+
The `RunTestsInCoroutine` trait overrides PHPUnit's `runBare()` method to:
78+
79+
1. **Check Prerequisites**: Verifies that the Swoole extension is loaded and not already in a coroutine context
80+
2. **Create Coroutine Context**: Wraps test execution in `Swoole\Coroutine\run()`
81+
3. **Exception Handling**: Properly captures and re-throws exceptions from within the coroutine
82+
4. **Cleanup**: Clears all timers and resumes coordinator on test completion
83+
5. **Fallback**: If conditions aren't met, falls back to normal test execution
84+
85+
### PHPUnit Patch
86+
87+
The package includes a `phpunit-patch.php` file that automatically removes the `final` keyword from PHPUnit's `TestCase::runBare()` method, allowing the trait to override it. This patch is applied automatically when the package is autoloaded.
88+
89+
## Requirements
90+
91+
- PHP >= 8.0
92+
- PHPUnit >= 10.0
93+
- Swoole extension (when running tests in coroutine mode)
94+
- Hyperf >= 3.1 (for coordinator functionality)
95+
96+
## Configuration
97+
98+
### Composer Autoload
99+
100+
The package automatically registers its autoload files in composer.json:
101+
102+
```json
103+
{
104+
"autoload-dev": {
105+
"psr-4": {
106+
"Your\\Tests\\": "tests/"
107+
},
108+
"files": [
109+
"vendor/friendsofhyperf/co-phpunit/phpunit-patch.php"
110+
]
111+
}
112+
}
113+
```
114+
115+
### PHPUnit Configuration
116+
117+
No special PHPUnit configuration is required. The package works seamlessly with your existing `phpunit.xml` configuration.
118+
119+
## Best Practices
120+
121+
1. **Use for Integration Tests**: This is particularly useful for integration tests that interact with Hyperf's coroutine-aware components
122+
2. **Selective Enablement**: Not all tests need to run in coroutines. Use `$enableCoroutine = false` for unit tests that don't require coroutine context
123+
3. **Test Isolation**: The package automatically cleans up timers and coordinator state between tests
124+
4. **Performance**: Tests running in coroutines may have slightly different performance characteristics
125+
126+
## Example: Testing Hyperf Services
127+
128+
```php
129+
<?php
130+
131+
namespace App\Tests;
132+
133+
use FriendsOfHyperf\CoPHPUnit\Concerns\RunTestsInCoroutine;
134+
use Hyperf\Context\ApplicationContext;
135+
use PHPUnit\Framework\TestCase;
136+
137+
class ServiceTest extends TestCase
138+
{
139+
use RunTestsInCoroutine;
140+
141+
public function testServiceWithCoroutineContext()
142+
{
143+
// Get service from container
144+
$service = ApplicationContext::getContainer()->get(YourService::class);
145+
146+
// Test methods that use coroutine context
147+
$result = $service->asyncOperation();
148+
149+
$this->assertNotNull($result);
150+
}
151+
152+
public function testDatabaseConnection()
153+
{
154+
// Test database operations that require connection pool
155+
$result = Db::table('users')->first();
156+
157+
$this->assertIsArray($result);
158+
}
159+
}
160+
```
161+
162+
## Troubleshooting
163+
164+
### Tests Hang or Timeout
165+
166+
If tests hang, ensure that:
167+
- All async operations are properly awaited
168+
- No infinite loops exist in coroutine callbacks
169+
- Timers are cleared in test teardown
170+
171+
### "Call to a member function on null"
172+
173+
This usually indicates that coroutine context is not available. Ensure:
174+
- Swoole extension is installed and enabled
175+
- The `RunTestsInCoroutine` trait is included
176+
- `$enableCoroutine` is set to `true`
177+
178+
### PHPUnit Version Compatibility
179+
180+
The package supports PHPUnit 10.x, 11.x, and 12.x. Ensure your PHPUnit version is compatible.
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# Command Benchmark
2+
3+
## Introduction
4+
5+
`friendsofhyperf/command-benchmark` is a benchmark component for Hyperf commands, forked from [christophrumpel/artisan-benchmark](https://github.com/christophrumpel/artisan-benchmark). It helps you analyze command execution time, memory usage, and SQL query count.
6+
7+
## Installation
8+
9+
```shell
10+
composer require friendsofhyperf/command-benchmark
11+
```
12+
13+
## Features
14+
15+
This component automatically adds performance monitoring to all Hyperf commands through AOP aspects, without modifying existing code. It automatically tracks:
16+
17+
- **Execution Time**: Total time from command start to finish
18+
- **Memory Usage**: Memory consumption during command execution
19+
- **SQL Query Count**: Total number of database queries executed during command execution
20+
21+
## Usage
22+
23+
### Basic Usage
24+
25+
After installing the component, all Hyperf commands automatically get an `--enable-benchmark` option. Simply add this option when running a command to enable benchmarking:
26+
27+
```shell
28+
php bin/hyperf.php your:command --enable-benchmark
29+
```
30+
31+
### Example Output
32+
33+
When benchmarking is enabled, performance statistics are displayed at the end of the output after command completion:
34+
35+
```
36+
⚡ TIME: 1.23s MEM: 2.45MB SQL: 15
37+
38+
```
39+
40+
Output explanation:
41+
- **TIME**: Command execution time (automatically formatted as minutes/seconds/milliseconds)
42+
- **MEM**: Memory usage (in MB)
43+
- **SQL**: Number of SQL queries executed
44+
45+
## How It Works
46+
47+
The component uses Hyperf's AOP (Aspect-Oriented Programming) functionality to intercept command execution:
48+
49+
1. **Constructor Interception**: Records start time and memory usage when command is instantiated
50+
2. **Event Listening**: Listens for database query events to count SQL queries
51+
3. **Execution Interception**: Calculates performance metrics after command execution completes
52+
4. **Result Display**: Outputs benchmark results in a colored format
53+
54+
### Time Formatting
55+
56+
Execution time automatically selects the most appropriate unit based on actual duration:
57+
58+
- **≥ 60 seconds**: Displayed as minutes and seconds (e.g., 2m 30s)
59+
- **≥ 1 second**: Displayed as seconds (e.g., 1.23s)
60+
- **< 1 second**: Displayed as milliseconds (e.g., 450ms)
61+
62+
## Configuration
63+
64+
This component works out of the box with no additional configuration needed. The `CommandAspect` aspect is automatically registered after installation.
65+
66+
If you need to customize behavior, you can modify or extend the `FriendsOfHyperf\CommandBenchmark\Aspect\CommandAspect` class.
67+
68+
## Examples
69+
70+
### Analyzing Database Migration Commands
71+
72+
```shell
73+
php bin/hyperf.php migrate --enable-benchmark
74+
```
75+
76+
Output:
77+
```
78+
Migration table created successfully.
79+
⚡ TIME: 2.5s MEM: 12.34MB SQL: 48
80+
```
81+
82+
### Analyzing Custom Commands
83+
84+
```shell
85+
php bin/hyperf.php app:process-data --enable-benchmark
86+
```
87+
88+
Output:
89+
```
90+
Data processing completed.
91+
⚡ TIME: 15.2s MEM: 45.67MB SQL: 1024
92+
```
93+
94+
## Best Practices
95+
96+
1. **Performance Optimization**: Use this component to identify slow-running commands and potential performance bottlenecks
97+
2. **SQL Optimization**: Monitor SQL query counts to identify N+1 query problems
98+
3. **Memory Monitoring**: Track memory usage to prevent memory leaks
99+
4. **Continuous Monitoring**: Use in CI/CD pipelines to ensure command performance doesn't degrade
100+
101+
## Technical Implementation
102+
103+
### Aspect Configuration
104+
105+
```php
106+
<?php
107+
108+
namespace FriendsOfHyperf\CommandBenchmark;
109+
110+
class ConfigProvider
111+
{
112+
public function __invoke()
113+
{
114+
return [
115+
'aspects' => [
116+
Aspect\CommandAspect::class,
117+
],
118+
];
119+
}
120+
}
121+
```
122+
123+
### Key Features
124+
125+
- **WeakMap Storage**: Uses `WeakMap` to store command performance metrics, avoiding memory leaks
126+
- **Event-Driven**: Counts SQL queries by listening to `QueryExecuted` events
127+
- **Non-Invasive**: No need to modify existing command code
128+
- **Automatic Cleanup**: Performance data is automatically cleaned up after command execution
129+
130+
## Notes
131+
132+
- The component intercepts all command constructor and execution methods, but only displays results when using the `--enable-benchmark` option
133+
- SQL query statistics depend on Hyperf's database component; if database is not used, this metric will always show 0
134+
- Performance data is only displayed after command execution completes; real-time monitoring is not supported
135+
136+
## References
137+
138+
- Original Project: [christophrumpel/artisan-benchmark](https://github.com/christophrumpel/artisan-benchmark)
139+
- Hyperf AOP Documentation: [https://hyperf.wiki/3.1/#/en/aop](https://hyperf.wiki/3.1/#/en/aop)

0 commit comments

Comments
 (0)