Skip to content

Commit 814e71f

Browse files
markshustclaude
andcommitted
fix(docs): correct observer and plugin examples across all docs
- Observer: use #[Observer(event: Class::class)] attribute instead of string event names or non-existent module.php 'observers' registration - Plugins: use #[Plugin(target:)], #[Before], #[After] attributes instead of non-existent module.php 'plugins' registration - Fix plugin method signatures: no $subject param, before returns null, after receives $result as first arg - All examples now match the actual core implementation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 394bca7 commit 814e71f

7 files changed

Lines changed: 78 additions & 96 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ class OrderPlugin
143143
**Observers** — React to system events:
144144

145145
```php
146-
#[Observer('order.placed')]
146+
#[Observer(event: OrderPlaced::class)]
147147
class SendOrderNotification
148148
{
149149
public function handle(OrderPlaced $event): void

docs/src/content/docs/concepts/events.md

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class PostCreatedEvent
5858

5959
## Creating Observers
6060

61-
An observer is a class that handles a specific event:
61+
An observer is a class with the `#[Observer]` attribute that handles a specific event. The `event` parameter specifies which event class to listen for:
6262

6363
```php title="TrackPostCreation.php"
6464
<?php
@@ -68,7 +68,9 @@ declare(strict_types=1);
6868
namespace App\Analytics\Observer;
6969

7070
use Marko\Blog\Event\PostCreatedEvent;
71+
use Marko\Core\Attributes\Observer;
7172

73+
#[Observer(event: PostCreatedEvent::class)]
7274
class TrackPostCreation
7375
{
7476
public function handle(PostCreatedEvent $event): void
@@ -80,31 +82,26 @@ class TrackPostCreation
8082
}
8183
```
8284

83-
## Registering Observers
85+
Observers are discovered automatically from module `src/` directories — no manual registration needed.
8486

85-
Observers are registered in `module.php`:
86-
87-
```php title="module.php"
88-
<?php
87+
## Observer Priority
8988

90-
declare(strict_types=1);
89+
When multiple observers listen to the same event, they run by priority (higher values first). Use the `priority` parameter to control order:
9190

92-
use App\Analytics\Observer\TrackPostCreation;
93-
use Marko\Blog\Event\PostCreatedEvent;
91+
```php
92+
#[Observer(event: PostCreatedEvent::class, priority: 10)]
93+
class HighPriorityObserver
94+
{
95+
public function handle(PostCreatedEvent $event): void { /* ... */ }
96+
}
9497

95-
return [
96-
'observers' => [
97-
PostCreatedEvent::class => [
98-
TrackPostCreation::class,
99-
],
100-
],
101-
];
98+
#[Observer(event: PostCreatedEvent::class, priority: 0)]
99+
class DefaultPriorityObserver
100+
{
101+
public function handle(PostCreatedEvent $event): void { /* ... */ }
102+
}
102103
```
103104

104-
## Observer Priority
105-
106-
When multiple observers listen to the same event, they run in module priority order (`app/``modules/``vendor/`). Within the same module, observers run in the order they're listed.
107-
108105
## Built-in Events
109106

110107
Marko packages dispatch events at meaningful points:

docs/src/content/docs/concepts/plugins.md

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Marko intentionally does **not** support Around plugins. Around plugins (which w
2020

2121
## Creating a Plugin
2222

23-
A plugin is a plain PHP class with methods that follow a naming convention:
23+
A plugin is a class with the `#[Plugin]` attribute targeting the class you want to intercept. Methods use `#[Before]` and `#[After]` attributes and follow a naming convention:
2424

2525
```php title="PostRepositoryPlugin.php"
2626
<?php
@@ -30,68 +30,64 @@ declare(strict_types=1);
3030
namespace App\MyApp\Plugin;
3131

3232
use Marko\Blog\Repository\PostRepository;
33+
use Marko\Core\Attributes\After;
34+
use Marko\Core\Attributes\Before;
35+
use Marko\Core\Attributes\Plugin;
3336

37+
#[Plugin(target: PostRepository::class)]
3438
class PostRepositoryPlugin
3539
{
3640
/**
37-
* Modify arguments before getPost() is called.
41+
* Runs before getPost() — return null to continue, or non-null to short-circuit.
3842
*/
39-
public function beforeGetPost(PostRepository $subject, int $id): array
43+
#[Before]
44+
public function beforeGetPost(int $id): null
4045
{
41-
// Log or transform the input
42-
return [$id]; // Return modified arguments as array
46+
// Log or validate the input
47+
return null;
4348
}
4449

4550
/**
46-
* Modify the return value after getPost() completes.
51+
* Runs after getPost() — receives the result, then the original arguments.
4752
*/
48-
public function afterGetPost(PostRepository $subject, array $result): array
53+
#[After]
54+
public function afterGetPost(array $result, int $id): array
4955
{
50-
// Add extra data to the result
56+
// Enrich the result
5157
$result['retrieved_at'] = time();
5258

5359
return $result;
5460
}
5561
}
5662
```
5763

64+
Plugins are discovered automatically from module `src/` directories — no manual registration needed.
65+
5866
### Method Naming
5967

6068
- `beforeMethodName` — runs before `methodName`
6169
- `afterMethodName` — runs after `methodName`
6270

63-
The first parameter is always `$subject` (the original object). For `before` plugins, remaining parameters match the original method's signature. Return an array of the (possibly modified) arguments.
64-
65-
For `after` plugins, the second parameter is the result from the original method. Return the (possibly modified) result.
71+
For `before` plugins, parameters match the original method's signature. Return `null` to continue to the original method, or return a non-null value to short-circuit and use that as the result.
6672

67-
## Registering Plugins
73+
For `after` plugins, the first parameter is the result from the original method, followed by the original arguments. Return the (possibly modified) result.
6874

69-
Plugins are registered in `module.php`:
70-
71-
```php title="module.php"
72-
<?php
75+
### Sort Order
7376

74-
declare(strict_types=1);
75-
76-
use App\MyApp\Plugin\PostRepositoryPlugin;
77-
use Marko\Blog\Repository\PostRepository;
77+
Use the `sortOrder` parameter to control the order when multiple plugins target the same method:
7878

79-
return [
80-
'plugins' => [
81-
PostRepository::class => [
82-
PostRepositoryPlugin::class,
83-
],
84-
],
85-
];
79+
```php
80+
#[Before(sortOrder: 10)]
81+
public function beforeGetPost(int $id): null { /* runs first */ }
8682
```
8783

8884
## Plugin Execution Order
8985

9086
When multiple plugins target the same method:
9187

92-
1. All `before` plugins run (in module priority order)
93-
2. The original method runs
94-
3. All `after` plugins run (in module priority order)
88+
1. All `before` plugins run (in sort order)
89+
2. The original method runs (unless a before plugin short-circuited)
90+
3. All `after` plugins run (in sort order)
9591

9692
## When to Use Plugins
9793

docs/src/content/docs/guides/authentication.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -214,17 +214,18 @@ The authentication system dispatches events you can observe:
214214
| `FailedLoginEvent` | Login attempt fails |
215215
| `PasswordResetEvent` | Password is reset |
216216

217-
```php title="module.php"
217+
```php title="app/security/src/Observer/LockoutAfterFailures.php"
218218
use Marko\Authentication\Event\FailedLoginEvent;
219-
use App\Security\Observer\LockoutAfterFailures;
219+
use Marko\Core\Attributes\Observer;
220220

221-
return [
222-
'observers' => [
223-
FailedLoginEvent::class => [
224-
LockoutAfterFailures::class,
225-
],
226-
],
227-
];
221+
#[Observer(event: FailedLoginEvent::class)]
222+
class LockoutAfterFailures
223+
{
224+
public function handle(FailedLoginEvent $event): void
225+
{
226+
// Lock the account after repeated failures...
227+
}
228+
}
228229
```
229230

230231
## Next Steps

docs/src/content/docs/packages/core.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,13 @@ Decouple "something happened" from "react to it":
7373
use Marko\Core\Attributes\Observer;
7474
use Marko\Core\Event\Event;
7575

76-
#[Observer(event: 'user.created')]
76+
#[Observer(event: UserCreatedEvent::class)]
7777
class SendWelcomeEmail
7878
{
7979
public function handle(
80-
Event $event,
80+
UserCreatedEvent $event,
8181
): void {
82-
$user = $event->data['user'];
82+
$user = $event->user;
8383
// Send email...
8484
}
8585
}
@@ -185,7 +185,7 @@ throw new MarkoException(
185185
#[Plugin(target: ClassName::class)] // Mark class as plugin
186186
#[Before] // Run before target method
187187
#[After] // Run after target method
188-
#[Observer(event: 'event.name')] // React to events
188+
#[Observer(event: EventClass::class)] // React to events
189189
#[Command(name: 'cmd:name', description: '')] // Register CLI command
190190
```
191191

docs/src/content/docs/tutorials/build-a-blog.md

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -167,23 +167,26 @@ Protect the comment form so only logged-in users can comment:
167167
composer require marko/authentication
168168
```
169169

170-
The blog package dispatches events you can observe:
170+
The blog package dispatches events you can observe. Create an observer class with the `#[Observer]` attribute:
171171

172-
```php title="app/blog/module.php"
172+
```php title="app/blog/src/Observer/NotifyAuthorOfComment.php"
173173
<?php
174174

175175
declare(strict_types=1);
176176

177+
namespace App\Blog\Observer;
178+
177179
use Marko\Blog\Events\Comment\CommentCreated;
178-
use App\Blog\Observer\NotifyAuthorOfComment;
179-
180-
return [
181-
'observers' => [
182-
CommentCreated::class => [
183-
NotifyAuthorOfComment::class,
184-
],
185-
],
186-
];
180+
use Marko\Core\Attributes\Observer;
181+
182+
#[Observer(event: CommentCreated::class)]
183+
class NotifyAuthorOfComment
184+
{
185+
public function handle(CommentCreated $event): void
186+
{
187+
// Send notification to the post author...
188+
}
189+
}
187190
```
188191

189192
## Step 7: Extend with Plugins
@@ -199,10 +202,14 @@ namespace App\Blog\Plugin;
199202

200203
use Marko\Blog\Entity\Post;
201204
use Marko\Blog\Repositories\PostRepositoryInterface;
205+
use Marko\Core\Attributes\After;
206+
use Marko\Core\Attributes\Plugin;
202207

208+
#[Plugin(target: PostRepositoryInterface::class)]
203209
class AddReadingTimePlugin
204210
{
205-
public function afterFindBySlug(PostRepositoryInterface $subject, ?Post $result): ?Post
211+
#[After]
212+
public function afterFindBySlug(?Post $result): ?Post
206213
{
207214
if ($result === null) {
208215
return null;
@@ -216,25 +223,6 @@ class AddReadingTimePlugin
216223
}
217224
```
218225

219-
Register it:
220-
221-
```php title="app/blog/module.php"
222-
<?php
223-
224-
declare(strict_types=1);
225-
226-
use Marko\Blog\Repositories\PostRepositoryInterface;
227-
use App\Blog\Plugin\AddReadingTimePlugin;
228-
229-
return [
230-
'plugins' => [
231-
PostRepositoryInterface::class => [
232-
AddReadingTimePlugin::class,
233-
],
234-
],
235-
];
236-
```
237-
238226
## What You've Learned
239227

240228
- How to scaffold a Marko project and install packages

packages/blog/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ Use `#[Observer]` to react to blog lifecycle events without modifying core class
104104
use Marko\Core\Attributes\Observer;
105105
use Marko\Blog\Events\Post\PostCreated;
106106

107-
#[Observer]
107+
#[Observer(event: PostCreated::class)]
108108
class PostCreatedObserver
109109
{
110110
public function handle(PostCreated $event): void

0 commit comments

Comments
 (0)