PHPackages                             juststeveking/signal - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. juststeveking/signal

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

juststeveking/signal
====================

v1.0.0(1mo ago)130[1 issues](https://github.com/JustSteveKing/signal/issues)MITPHPPHP ^8.5CI passing

Since Apr 29Pushed 1mo agoCompare

[ Source](https://github.com/JustSteveKing/signal)[ Packagist](https://packagist.org/packages/juststeveking/signal)[ GitHub Sponsors](https://github.com/juststeveking)[ RSS](/packages/juststeveking-signal/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (1)Dependencies (6)Versions (2)Used By (0)

Signal
======

[](#signal)

[![Tests](https://github.com/JustSteveKing/signal/actions/workflows/tests.yml/badge.svg)](https://github.com/JustSteveKing/signal/actions/workflows/tests.yml)

**Signal** is a PHP library that turns PHP attributes into living documentation. Annotate your classes and methods with Signal's attributes, then run a single command to generate Markdown and JSON docs that always reflect your actual code.

No more docs that drift. No more manually maintained API references. Signal reads your annotations and writes the documentation for you.

---

Requirements
------------

[](#requirements)

- PHP 8.5+
- Symfony Console

---

Installation
------------

[](#installation)

```
composer require juststeveking/signal
```

---

Quick Start
-----------

[](#quick-start)

**1. Create a `signal.json` config at your project root:**

```
{
    "input": "src/",
    "output": {
        "format": ["markdown", "json"],
        "path": "docs/"
    },
    "exclude": [
        "src/Attributes/"
    ]
}
```

**2. Annotate your classes:**

```
use JustSteveKing\Signal\Attributes\Service;
use JustSteveKing\Signal\Attributes\Deprecated;
use JustSteveKing\Signal\Attributes\DependsOn;
use JustSteveKing\Signal\Attributes\Route;
use JustSteveKing\Signal\Attributes\Authorize;
use JustSteveKing\Signal\Attributes\Throws;

#[Service(
    description: 'Handles order processing and payment workflows',
    tags: ['orders', 'payments'],
)]
#[DependsOn(class: PaymentGateway::class, description: 'Required for processing payments')]
#[DependsOn(class: OrderRepository::class)]
final class OrderService
{
    #[Route(method: 'POST', path: '/orders', description: 'Create a new order')]
    #[Authorize(ability: 'orders.create')]
    #[Validates(field: 'items', rules: 'required|array|min:1')]
    #[Validates(field: 'payment_method', rules: 'required|string')]
    #[Emits(event: 'OrderPlaced', description: 'Fired after successful order creation')]
    #[Throws(exception: PaymentFailedException::class, description: 'When payment processing fails')]
    public function store(Request $request): OrderResource
    {
        // ...
    }
}
```

**3. Generate documentation:**

```
php vendor/bin/signal generate
```

Signal scans your `src/` directory, reflects on every annotated class, and writes `docs/signal.md` and `docs/signal.json`.

---

Configuration
-------------

[](#configuration)

KeyTypeDescription`input``string`Directory to scan (relative to config file)`output.format``string[]`Output formats: `markdown`, `json`, or both`output.path``string`Directory to write generated files`exclude``string[]`Paths to skip during scanning---

Attributes
----------

[](#attributes)

Signal provides 24 attributes split into three groups: **class type** attributes that identify what a class is, **class metadata** attributes that describe relationships and status, and **method** attributes that document individual methods.

---

### Class Type Attributes

[](#class-type-attributes)

These attributes appear on a class declaration and tell Signal what kind of class it is. Each accepts a `description` string and a `tags` array.

#### `#[Module]`

[](#module)

Top-level application module grouping related functionality.

```
use JustSteveKing\Signal\Attributes\Module;

#[Module(description: 'Handles everything related to billing', tags: ['billing'])]
final class BillingModule {}
```

#### `#[Service]`

[](#service)

Application or domain service containing business logic.

```
use JustSteveKing\Signal\Attributes\Service;

#[Service(description: 'Processes subscription renewals', tags: ['subscriptions', 'billing'])]
final class SubscriptionRenewalService {}
```

#### `#[Repository]`

[](#repository)

Data access layer class that wraps persistence.

```
use JustSteveKing\Signal\Attributes\Repository;

#[Repository(description: 'Reads and writes Order records', tags: ['orders', 'persistence'])]
final class OrderRepository {}
```

#### `#[Action]`

[](#action)

Single-purpose action class (use case / interactor).

```
use JustSteveKing\Signal\Attributes\Action;

#[Action(description: 'Places a new customer order', tags: ['orders'])]
final class PlaceOrderAction {}
```

#### `#[Controller]`

[](#controller)

HTTP controller that handles incoming requests.

```
use JustSteveKing\Signal\Attributes\Controller;

#[Controller(description: 'Manages order CRUD endpoints', tags: ['orders', 'api'])]
final class OrderController {}
```

#### `#[Event]`

[](#event)

Domain event representing something that happened.

```
use JustSteveKing\Signal\Attributes\Event;

#[Event(description: 'Fired when a customer places an order', tags: ['orders', 'events'])]
final class OrderPlacedEvent {}
```

#### `#[Listener]`

[](#listener)

Event listener that reacts to one or more domain events.

```
use JustSteveKing\Signal\Attributes\Listener;
use JustSteveKing\Signal\Attributes\ListensTo;

#[Listener(description: 'Sends a confirmation email after order is placed', tags: ['orders', 'email'])]
#[ListensTo(event: 'OrderPlaced', description: 'Triggers the confirmation email')]
final class SendOrderConfirmationListener {}
```

#### `#[Middleware]`

[](#middleware)

HTTP middleware with an optional execution priority.

```
use JustSteveKing\Signal\Attributes\Middleware;

#[Middleware(description: 'Validates the Bearer token on every request', tags: ['auth'], priority: 10)]
final class AuthMiddleware {}
```

The `priority` integer controls rendering order in the generated docs (lower = earlier).

#### `#[Job]`

[](#job)

Queueable background job with an optional target queue name.

```
use JustSteveKing\Signal\Attributes\Job;

#[Job(description: 'Sends the customer invoice PDF via email', tags: ['billing'], queue: 'invoices')]
final class SendInvoiceJob {}
```

#### `#[Command]`

[](#command)

Console / CLI command.

```
use JustSteveKing\Signal\Attributes\Command;

#[Command(description: 'Recalculates all open subscription invoices', tags: ['billing', 'cli'])]
final class RecalculateInvoicesCommand {}
```

#### `#[Query]`

[](#query)

Read-only query class (CQRS query side).

```
use JustSteveKing\Signal\Attributes\Query;

#[Query(description: 'Fetches a paginated list of orders for the current user', tags: ['orders', 'cqrs'])]
final class GetUserOrdersQuery {}
```

#### `#[Aggregate]`

[](#aggregate)

DDD aggregate root that owns a consistency boundary.

```
use JustSteveKing\Signal\Attributes\Aggregate;

#[Aggregate(description: 'Order aggregate root managing the full order lifecycle', tags: ['orders', 'ddd'])]
final class OrderAggregate {}
```

#### `#[ValueObject]`

[](#valueobject)

DDD value object — immutable, identity-less.

```
use JustSteveKing\Signal\Attributes\ValueObject;

#[ValueObject(description: 'Represents a monetary amount with currency', tags: ['money', 'ddd'])]
final class Money {}
```

---

### Class Metadata Attributes

[](#class-metadata-attributes)

These attributes provide additional context about a class — its dependencies, the events it handles, and whether it's deprecated or internal-only.

#### `#[DependsOn]` *(repeatable)*

[](#dependson-repeatable)

Declares an explicit dependency on another class.

```
use JustSteveKing\Signal\Attributes\Service;
use JustSteveKing\Signal\Attributes\DependsOn;

#[Service(description: 'Handles payment processing')]
#[DependsOn(class: StripeGateway::class, description: 'Primary payment provider')]
#[DependsOn(class: FraudDetectionService::class)]
final class PaymentService {}
```

#### `#[ListensTo]` *(repeatable)*

[](#listensto-repeatable)

Declares which domain events a listener class handles.

```
use JustSteveKing\Signal\Attributes\Listener;
use JustSteveKing\Signal\Attributes\ListensTo;

#[Listener(description: 'Handles post-order events')]
#[ListensTo(event: 'OrderPlaced', description: 'Sends confirmation email', tags: ['email'])]
#[ListensTo(event: 'OrderCancelled', description: 'Sends cancellation notice', tags: ['email'])]
final class OrderNotificationListener {}
```

#### `#[Deprecated]`

[](#deprecated)

Marks a class or method as deprecated with an optional version and reason.

```
use JustSteveKing\Signal\Attributes\Service;
use JustSteveKing\Signal\Attributes\Deprecated;

#[Service(description: 'Legacy order sync service')]
#[Deprecated(reason: 'Replaced by OrderSyncV2Service', since: '2.0.0')]
final class OrderSyncService {}
```

`#[Deprecated]` works on methods too:

```
#[Deprecated(reason: 'Use createOrder() instead', since: '1.5.0')]
public function placeOrder(array $data): Order {}
```

#### `#[Internal]`

[](#internal)

Marks a class or method as internal — not part of the public API.

```
use JustSteveKing\Signal\Attributes\Internal;

#[Internal(reason: 'Framework bootstrap only — do not use directly')]
final class KernelBootstrapper {}
```

---

### Method Attributes

[](#method-attributes)

These attributes live on individual methods and document the HTTP layer, access control, validation, caching, events, exceptions, and side effects.

#### `#[Route]`

[](#route)

Binds a method to an HTTP verb and path.

```
use JustSteveKing\Signal\Attributes\Route;

#[Route(method: 'GET', path: '/orders', description: 'List all orders for the authenticated user')]
public function index(): JsonResponse {}

#[Route(method: 'POST', path: '/orders')]
public function store(Request $request): JsonResponse {}
```

#### `#[Authorize]` *(repeatable)*

[](#authorize-repeatable)

Declares the authorization ability required to call this method.

```
use JustSteveKing\Signal\Attributes\Authorize;

#[Authorize(ability: 'orders.view', description: 'User must own the order or be an admin')]
public function show(Order $order): JsonResponse {}

// Multiple abilities:
#[Authorize(ability: 'orders.update')]
#[Authorize(ability: 'orders.approve')]
public function approve(Order $order): JsonResponse {}
```

#### `#[Validates]` *(repeatable)*

[](#validates-repeatable)

Documents the validation rules applied to request fields.

```
use JustSteveKing\Signal\Attributes\Validates;

#[Validates(field: 'email', rules: 'required|email', description: 'Customer email address')]
#[Validates(field: 'items', rules: 'required|array|min:1')]
#[Validates(field: 'coupon_code', rules: 'nullable|string|max:20')]
public function store(Request $request): JsonResponse {}
```

#### `#[Cached]`

[](#cached)

Documents that a method's result is cached, with optional TTL and cache key.

```
use JustSteveKing\Signal\Attributes\Cached;

#[Cached(ttl: 300, key: 'orders.user.{userId}', description: 'Cached per user for 5 minutes')]
public function forUser(int $userId): Collection {}

// Default TTL is 3600 seconds
#[Cached]
public function all(): Collection {}
```

#### `#[Emits]` *(repeatable)*

[](#emits-repeatable)

Documents domain events dispatched by this method.

```
use JustSteveKing\Signal\Attributes\Emits;

#[Emits(event: 'OrderPlaced', description: 'Fired after the order is persisted', tags: ['orders'])]
#[Emits(event: 'StockReserved', tags: ['inventory'])]
public function store(Request $request): JsonResponse {}
```

#### `#[Throws]` *(repeatable)*

[](#throws-repeatable)

Documents exceptions this method may throw.

```
use JustSteveKing\Signal\Attributes\Throws;

#[Throws(exception: PaymentFailedException::class, description: 'When the payment gateway rejects the charge')]
#[Throws(exception: InsufficientStockException::class)]
public function checkout(Cart $cart): Order {}
```

#### `#[SideEffect]` *(repeatable)*

[](#sideeffect-repeatable)

Documents observable side effects beyond the return value.

```
use JustSteveKing\Signal\Attributes\SideEffect;

#[SideEffect(description: 'Sends an order confirmation email to the customer', tags: ['email'])]
#[SideEffect(description: 'Decrements inventory for each item in the order', tags: ['inventory'])]
public function store(Request $request): JsonResponse {}
```

---

A Complete Example
------------------

[](#a-complete-example)

Here is a realistic controller with Signal annotations showing how the attributes compose together:

```
