acc-create-psr14-event-dispatcher

Generates PSR-14 Event Dispatcher implementation for PHP 8.5. Creates EventDispatcherInterface, ListenerProviderInterface, and StoppableEventInterface with event propagation. Includes unit tests.

181 stars

Best use case

acc-create-psr14-event-dispatcher is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Generates PSR-14 Event Dispatcher implementation for PHP 8.5. Creates EventDispatcherInterface, ListenerProviderInterface, and StoppableEventInterface with event propagation. Includes unit tests.

Teams using acc-create-psr14-event-dispatcher should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/acc-create-psr14-event-dispatcher/SKILL.md --create-dirs "https://raw.githubusercontent.com/majiayu000/claude-skill-registry/main/skills/data/acc-create-psr14-event-dispatcher/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/acc-create-psr14-event-dispatcher/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How acc-create-psr14-event-dispatcher Compares

Feature / Agentacc-create-psr14-event-dispatcherStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Generates PSR-14 Event Dispatcher implementation for PHP 8.5. Creates EventDispatcherInterface, ListenerProviderInterface, and StoppableEventInterface with event propagation. Includes unit tests.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# PSR-14 Event Dispatcher Generator

## Overview

Generates PSR-14 compliant event dispatcher for domain events and application events.

## When to Use

- Implementing event-driven architecture
- Domain event dispatching in DDD
- Decoupling application components
- Building CQRS systems

## Generated Components

| Component | Description | Location |
|-----------|-------------|----------|
| EventDispatcher | Dispatches events | `src/Infrastructure/Event/` |
| ListenerProvider | Provides listeners | `src/Infrastructure/Event/` |
| Stoppable Event | Base stoppable event | `src/Infrastructure/Event/` |
| Unit Tests | PHPUnit tests | `tests/Unit/Infrastructure/Event/` |

## Template: Event Dispatcher

```php
<?php

declare(strict_types=1);

namespace App\Infrastructure\Event;

use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\EventDispatcher\ListenerProviderInterface;
use Psr\EventDispatcher\StoppableEventInterface;

final readonly class EventDispatcher implements EventDispatcherInterface
{
    public function __construct(
        private ListenerProviderInterface $listenerProvider,
    ) {
    }

    public function dispatch(object $event): object
    {
        foreach ($this->listenerProvider->getListenersForEvent($event) as $listener) {
            if ($event instanceof StoppableEventInterface && $event->isPropagationStopped()) {
                break;
            }

            $listener($event);
        }

        return $event;
    }
}
```

## Template: Listener Provider

```php
<?php

declare(strict_types=1);

namespace App\Infrastructure\Event;

use Psr\EventDispatcher\ListenerProviderInterface;

final class ListenerProvider implements ListenerProviderInterface
{
    /** @var array<class-string, array<callable>> */
    private array $listeners = [];

    public function getListenersForEvent(object $event): iterable
    {
        $eventClass = $event::class;

        yield from $this->listeners[$eventClass] ?? [];

        foreach (class_parents($eventClass) as $parent) {
            yield from $this->listeners[$parent] ?? [];
        }

        foreach (class_implements($eventClass) as $interface) {
            yield from $this->listeners[$interface] ?? [];
        }
    }

    /** @param class-string $eventClass */
    public function addListener(string $eventClass, callable $listener): void
    {
        $this->listeners[$eventClass][] = $listener;
    }
}
```

## Template: Stoppable Event

```php
<?php

declare(strict_types=1);

namespace App\Infrastructure\Event;

use Psr\EventDispatcher\StoppableEventInterface;

abstract class StoppableEvent implements StoppableEventInterface
{
    private bool $propagationStopped = false;

    public function isPropagationStopped(): bool
    {
        return $this->propagationStopped;
    }

    public function stopPropagation(): void
    {
        $this->propagationStopped = true;
    }
}
```

## Template: Domain Event

```php
<?php

declare(strict_types=1);

namespace App\Domain\User\Event;

use App\Domain\User\ValueObject\UserId;
use DateTimeImmutable;

final readonly class UserCreated
{
    public function __construct(
        public UserId $userId,
        public string $email,
        public DateTimeImmutable $occurredAt = new DateTimeImmutable(),
    ) {
    }
}
```

## Template: Event Listener

```php
<?php

declare(strict_types=1);

namespace App\Application\User\Listener;

use App\Domain\User\Event\UserCreated;
use Psr\Log\LoggerInterface;

final readonly class SendWelcomeEmailListener
{
    public function __construct(
        private EmailServiceInterface $emailService,
        private LoggerInterface $logger,
    ) {
    }

    public function __invoke(UserCreated $event): void
    {
        $this->logger->info('Sending welcome email', [
            'user_id' => $event->userId->toString(),
            'email' => $event->email,
        ]);

        $this->emailService->send(
            to: $event->email,
            subject: 'Welcome!',
            template: 'emails/welcome',
        );
    }
}
```

## Template: Unit Test

```php
<?php

declare(strict_types=1);

namespace App\Tests\Unit\Infrastructure\Event;

use App\Infrastructure\Event\EventDispatcher;
use App\Infrastructure\Event\ListenerProvider;
use App\Infrastructure\Event\StoppableEvent;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

#[Group('unit')]
#[CoversClass(EventDispatcher::class)]
final class EventDispatcherTest extends TestCase
{
    #[Test]
    public function it_dispatches_event_to_listeners(): void
    {
        $provider = new ListenerProvider();
        $dispatcher = new EventDispatcher($provider);

        $called = false;
        $provider->addListener(TestEvent::class, function () use (&$called) {
            $called = true;
        });

        $dispatcher->dispatch(new TestEvent());

        self::assertTrue($called);
    }

    #[Test]
    public function it_stops_propagation_for_stoppable_events(): void
    {
        $provider = new ListenerProvider();
        $dispatcher = new EventDispatcher($provider);

        $callCount = 0;
        $provider->addListener(TestStoppableEvent::class, function (TestStoppableEvent $e) use (&$callCount) {
            $callCount++;
            $e->stopPropagation();
        });
        $provider->addListener(TestStoppableEvent::class, function () use (&$callCount) {
            $callCount++;
        });

        $dispatcher->dispatch(new TestStoppableEvent());

        self::assertSame(1, $callCount);
    }

    #[Test]
    public function it_returns_event_after_dispatch(): void
    {
        $provider = new ListenerProvider();
        $dispatcher = new EventDispatcher($provider);

        $event = new TestEvent();
        $result = $dispatcher->dispatch($event);

        self::assertSame($event, $result);
    }
}

final class TestEvent {}
final class TestStoppableEvent extends StoppableEvent {}
```

## Usage Example

```php
<?php

use App\Infrastructure\Event\EventDispatcher;
use App\Infrastructure\Event\ListenerProvider;

// Setup
$provider = new ListenerProvider();
$dispatcher = new EventDispatcher($provider);

// Register listeners
$provider->addListener(
    UserCreated::class,
    new SendWelcomeEmailListener($emailService, $logger),
);

$provider->addListener(
    UserCreated::class,
    new CreateUserProfileListener($profileService),
);

// Dispatch event (from domain entity or handler)
$event = new UserCreated($userId, $email);
$dispatcher->dispatch($event);
```

## DDD Integration

```php
<?php

declare(strict_types=1);

namespace App\Domain\User\Entity;

use App\Domain\User\Event\UserCreated;
use App\Domain\User\ValueObject\Email;
use App\Domain\User\ValueObject\UserId;

final class User
{
    /** @var object[] */
    private array $events = [];

    private function __construct(
        private readonly UserId $id,
        private Email $email,
    ) {
    }

    public static function create(Email $email): self
    {
        $user = new self(UserId::generate(), $email);
        $user->events[] = new UserCreated($user->id, $email->toString());

        return $user;
    }

    /** @return object[] */
    public function pullEvents(): array
    {
        $events = $this->events;
        $this->events = [];

        return $events;
    }
}
```

## Requirements

```json
{
    "require": {
        "psr/event-dispatcher": "^1.0"
    }
}
```

## See Also

- `references/templates.md` - Priority provider, async dispatcher
- `references/examples.md` - Integration examples

Related Skills

adding-persistent-event

181
from majiayu000/claude-skill-registry

Adds a new type of event that gets persisted to the event log. Use this when adding new kinds of write operations to the system or when adding new events to existing code.

add-resource-events

181
from majiayu000/claude-skill-registry

Add real-time event emission to a resource service. Use when adding SSE/real-time capabilities to a resource. Triggers on "add events", "real-time events", "SSE events".

add-event-type

181
from majiayu000/claude-skill-registry

Add a new event type to the frontend feed system with corresponding React component. Use when user mentions "new event", "add event type", "event block", "new block type", or wants to display new agent output types.

acc-event-sourcing-knowledge

181
from majiayu000/claude-skill-registry

Event Sourcing knowledge base. Provides patterns, antipatterns, and PHP-specific guidelines for Event Sourcing architecture audits.

acc-create-value-object

181
from majiayu000/claude-skill-registry

Generates DDD Value Objects for PHP 8.5. Creates immutable, self-validating objects with equality comparison. Includes unit tests.

acc-create-use-case

181
from majiayu000/claude-skill-registry

Generates Application Use Cases for PHP 8.5. Creates orchestration services that coordinate domain objects, handle transactions, and dispatch events. Includes unit tests.

acc-create-unit-test

181
from majiayu000/claude-skill-registry

Generates PHPUnit unit tests for PHP 8.5. Creates isolated tests with AAA pattern, proper naming, attributes, and one behavior per test. Supports Value Objects, Entities, Services.

acc-create-test-double

181
from majiayu000/claude-skill-registry

Generates test doubles (Mocks, Stubs, Fakes, Spies) for PHP 8.5. Creates appropriate double type based on testing needs with PHPUnit MockBuilder patterns.

acc-create-test-builder

181
from majiayu000/claude-skill-registry

Generates Test Data Builder and Object Mother patterns for PHP 8.5. Creates fluent builders with sensible defaults and factory methods for test data creation.

acc-create-strategy

181
from majiayu000/claude-skill-registry

Generates Strategy pattern for PHP 8.5. Creates interchangeable algorithm families with context class, strategy interface, and concrete implementations. Includes unit tests.

acc-create-state

181
from majiayu000/claude-skill-registry

Generates State pattern for PHP 8.5. Creates state machines with context, state interface, and concrete states for behavior changes. Includes unit tests.

acc-create-specification

181
from majiayu000/claude-skill-registry

Generates DDD Specification for PHP 8.5. Creates reusable business rule objects for validation, filtering, and querying with composite pattern support. Includes unit tests.