Extensions

Extend phpspec with custom matchers, formatters, commands, and event listeners. Extensions are Composer packages registered in phpspec.yaml.

Quick start

  1. Install the package: composer require --dev acme/phpspec-nyan-formatter
  2. Register in phpspec.yaml:
extensions:
  formatters:
    - Acme\NyanFormatter
  1. Use it: bin/phpspec run --format nyan

Configuration

extensions:
  matchers:
    - App\ValidJsonMatcher
  formatters:
    - App\MinimalFormatter
  commands:
    - App\GreetCommand
  listeners:
    - App\SlowTestReporter

Matcher extensions

Custom matchers extend MatcherExtension:

use PhpSpec\Extensions\MatcherExtension;

class ValidJsonMatcher extends MatcherExtension
{
    public function getName(): string
    {
        return 'toBeValidJson';
    }

    public function match(mixed $actual, mixed ...$args): bool
    {
        if (!is_string($actual)) { return false; }
        json_decode($actual);
        return json_last_error() === JSON_ERROR_NONE;
    }

    public function failureMessage(mixed $actual): string
    {
        return 'Expected valid JSON';
    }
}

Use in specs:

expect('{"name": "phpspec"}')->toBeValidJson();
expect('not json')->not()->toBeValidJson();

Formatter extensions

class MinimalFormatter extends FormatterExtension
{
    public function getName(): string { return 'minimal'; }

    public function formatPass(string $title): string
    {
        return "OK: $title";
    }

    public function formatFail(string $title, string $message): string
    {
        return "FAIL: $title - $message";
    }
}

Use with --format minimal or set as default in config.

Command extensions

use PhpSpec\Extensions\CommandExtension;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class GreetCommand extends CommandExtension
{
    public function getName(): string { return 'greet'; }
    public function getDescription(): string { return 'Say hello'; }

    public function execute(InputInterface $input, OutputInterface $output): int
    {
        $output->writeln('Hello from extension!');
        return 0;
    }
}

Run with: bin/phpspec greet

Listener extensions

React to lifecycle events:

class SlowTestReporter extends ListenerExtension
{
    private array $slow = [];

    public function afterExample(string $title, float $duration): void
    {
        if ($duration > 0.5) {
            $this->slow[] = [$title, $duration];
        }
    }

    public function afterSuite(): void
    {
        foreach ($this->slow as [$title, $duration]) {
            printf("  %.3fs  %s\n", $duration, $title);
        }
    }
}

Available events

EventArguments
beforeSuite()
afterSuite()
beforeSpec(string $title)Spec file title
afterSpec(string $title)Spec file title
beforeExample(string $title)Example title
afterExample(string $title, float $duration)Title + duration
onPass(string $title)Passed example
onFail(string $title, string $message)Failed example
onError(string $title, string $message)Errored example
onPending(string $title)Pending example
onSkipped(string $title)Skipped example

Auto-discovery

Extensions can opt into auto-discovery via composer.json:

{
    "extra": {
        "phpspec": {
            "formatters": ["Acme\\NyanFormatter"]
        }
    }
}

Disable auto-discovery for a specific package:

extensions:
  disabled:
    - acme/phpspec-nyan-formatter

Extension interfaces

TypeBase classRequired methods
FormatterFormatterExtensiongetName(), formatPass(), formatFail()
MatcherMatcherExtensiongetName(), match(), failureMessage()
CommandCommandExtensiongetName(), execute()
ListenerListenerExtensionAt least one event method

All base classes live in PhpSpec\Extensions\.