Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@ jobs:
build:
strategy:
matrix:
php: ['8.0', '8.1', '8.2', '8.3']
include:
- php: '8.0'
php: ['8.3', '8.4', '8.5']

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Setup PHP with fail-fast
uses: shivammathur/setup-php@v2
with:
Expand All @@ -31,19 +29,20 @@ jobs:

- name: Cache Composer packages
id: composer-cache
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: vendor
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
key: ${{ runner.os }}-php-${{ matrix.php }}-${{ hashFiles('composer.json') }}
restore-keys: |
${{ runner.os }}-php-
${{ runner.os }}-php-${{ matrix.php }}-

- name: Install dependencies
if: steps.composer-cache.outputs.cache-hit != 'true'
run: composer install --prefer-dist --no-progress --no-suggest
run: composer install --prefer-dist --no-progress

- name: Run test suite
run: |
mkdir -p build/logs
composer run rector:dry
composer run stan
composer run test
7 changes: 6 additions & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ jobs:
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'

- name: Build docs
run: |
Expand Down
16 changes: 16 additions & 0 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

$finder = PhpCsFixer\Finder::create()
->in(__DIR__ . '/src');

return (new PhpCsFixer\Config())
->setRules([
'@PSR12' => true,
// Import fully-qualified class names and shorten the references to the short name.
'fully_qualified_strict_types' => ['import_symbols' => true],
'no_unused_imports' => true,
'ordered_imports' => ['sort_algorithm' => 'alpha'],
])
->setFinder($finder);
8 changes: 5 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Use the official PHP 8 image as the base image
FROM php:8.1-fpm
FROM php:8.3-fpm

# Set the working directory
WORKDIR /var/www/html
Expand All @@ -21,15 +21,17 @@ RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip
RUN pecl install xdebug \
&& docker-php-ext-enable xdebug

# Write xdebug settings to a separate ini file so the `zend_extension=` line
# generated by docker-php-ext-enable above is preserved (not overwritten).
RUN { \
echo "xdebug.mode=coverage"; \
echo "xdebug.start_with_request=yes"; \
echo "xdebug.client_host=host.docker.internal"; \
echo "xdebug.client_port=9000"; \
} > /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini; \
} > /usr/local/etc/php/conf.d/xdebug-settings.ini

# Install Composer (a PHP package manager)
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer

# Mount a local folder to /var/www/html
VOLUME ./:/var/www/html
Expand Down
37 changes: 33 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,21 @@
}
],
"require": {
"php": "^8.0|^8.1|^8.2",
"php": "^8.3",
"psr/container": "^2.0",
"psr/event-dispatcher": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^9.6",
"phpstan/phpstan": "^1.10"
"phpunit/phpunit": "^12.0",
"phpstan/phpstan": "^2.0",
"rector/rector": "^2.0",
"friendsofphp/php-cs-fixer": "^3.34",
"illuminate/support": "^11.0 || ^12.0 || ^13.0",
"illuminate/contracts": "^11.0 || ^12.0 || ^13.0",
"orchestra/testbench": "^10.0 || ^11.0"
},
"suggest": {
"illuminate/support": "Required to use the Laravel integration (SiriusInvokatorServiceProvider, Invokator facade, do_* helpers, Blade directives)"
},
"autoload": {
"files": [
Expand All @@ -33,18 +41,39 @@
"Sirius\\Invokator\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Sirius\\Invokator\\Tests\\Laravel\\": "tests/Laravel/"
}
},
"provide": {
"psr/event-dispatcher-implementation": "1.0"
},
"extra": {
"laravel": {
"providers": [
"Sirius\\Invokator\\Laravel\\SiriusInvokatorServiceProvider"
],
"aliases": {
"Invokator": "Sirius\\Invokator\\Laravel\\Facades\\Invokator"
}
}
},
"scripts": {
"stan": [
"php vendor/bin/phpstan analyse"
],
"csfix": [
"tools/php-cs-fixer/vendor/bin/php-cs-fixer fix --standard=PSR-2 src"
"php vendor/bin/php-cs-fixer fix"
],
"test": [
"php vendor/bin/phpunit -c tests/phpunit.xml"
],
"rector": [
"php vendor/bin/rector process"
],
"rector:dry": [
"php vendor/bin/rector process --dry-run"
]
}
}
4 changes: 1 addition & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
version: '3'

services:

### PHP ###################################################
php:
build:
context: ./
container_name: stack_runner_php
container_name: siriusphp_invokator
volumes:
- ./:/var/www/html:delegated
ports:
Expand Down
139 changes: 139 additions & 0 deletions docs/7_laravel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
---
title: Using Sirius\Invokator with Laravel
---

# Laravel integration

Sirius\Invokator ships an optional Laravel bridge that exposes the library through
idiomatic Laravel surfaces: a service provider, an `Invokator` facade, a set of `do_*`
helper functions and a Blade directive. The core library stays framework-agnostic — the
bridge only loads inside a Laravel application.

## Installation

```bash
composer require siriusphp/invokator
```

The package is auto-discovered, so there is nothing to register manually. Laravel reads the
`extra.laravel` section of `composer.json` and registers:

- the `Sirius\Invokator\Laravel\SiriusInvokatorServiceProvider` service provider, and
- the `Invokator` facade alias.

The service provider injects Laravel's container into the `Invoker` as its PSR-11 container
(Laravel's container implements `Psr\Container\ContainerInterface`), and registers the
processors and the event dispatcher as **singletons** so that registrations made during
boot persist for the whole request.

## Defining vs. running

Every pattern uses the same overloaded call: pass **only the identifier** to get a builder
you can `->add()` callables to, or pass **extra arguments** to run it.

```php
use Sirius\Invokator\Laravel\Facades\Invokator;

// define
Invokator::pipeline('process-order')
->add(fn ($order) => /* ... */ $order)
->add(OrderNormalizer::class . '@handle');

// run
$result = Invokator::pipeline('process-order', $order);
```

`add()` accepts an optional priority (higher runs first) and, for actions and filters, an
optional argument limit: `->add($callable, $priority = 0, $argumentsLimit = 1)`.

> Running a pattern with **no** arguments is not expressible through this overload, because
> `Invokator::pipeline('id')` returns the builder. For that rare case resolve the processor
> directly, e.g. `app(\Sirius\Invokator\Processors\PipelineProcessor::class)->process('id')`.

## The `Invokator` facade

### Pipelines, actions, filters, middlewares

```php
// Pipeline — the result of each callable is the only argument to the next
Invokator::pipeline('slugify')
->add(fn ($title) => trim($title))
->add(fn ($title) => strtolower($title));
Invokator::pipeline('slugify', ' Hello World '); // "hello world"

// Filter — transform a value (extra arguments are kept for every callable)
Invokator::filter('price')->add(fn ($amount) => $amount * 1.2);
Invokator::filter('price', 100); // 120

// Action — run callables for their side effects; returns null
Invokator::action('analytics')->add(fn ($user) => Analytics::track($user));
Invokator::action('analytics', $user);

// Middleware — each callable receives the arguments plus a $next callback
Invokator::middleware('http')
->add(fn ($request, $next) => $next($request))
->add(fn ($request, $next) => /* terminal */ $request);
Invokator::middleware('http', $request);
```

### Events (PSR-14)

Events keep PSR-14 semantics: you dispatch an event **object** and the listener key is taken
from its class name (or `HasEventName::getEventName()`).

```php
// subscribe
Invokator::event(OrderPlaced::class)->add(function (OrderPlaced $event) {
// ...
});

// dispatch
Invokator::dispatch(new OrderPlaced($order));
```

Events that implement `Psr\EventDispatcher\StoppableEventInterface` (for instance via the
`Sirius\Invokator\Event\Stoppable` trait) stop propagation as usual.

## Helper functions

The same operations are available as global helpers, prefixed with `do_` to avoid clashing
with Laravel's built-in `event()` and `action()` helpers. They are loaded by the service
provider, so they only exist inside a Laravel application.

```php
do_pipeline('process-order')->add(/* ... */);
do_pipeline('process-order', $order);

do_filter('price', 100);
do_action('analytics', $user);
do_middleware('http', $request);

do_event(new OrderPlaced($order));
```

## Blade

Use the `@do_action` directive to run an action from a template (it emits nothing), and the
`do_filter()` helper inside an echo to print a filtered value:

```blade
<html>
<head>
@do_action('html-head', $page)
<title>{{ do_filter('page-title', $title) }}</title>
</head>
...
</html>
```

## Resolving callables

Because the `Invoker` is wired to Laravel's container, string callables are resolved through
it. The following all work:

- **Closures** — `fn ($x) => ...`.
- **`Service@method`** — the `Service` is resolved from the container *with dependency
injection*, then `method` is called.
- **Bound service ids / invokable services** registered in the container.
- **Plain function names** (`'trim'`) and **`Class::method`** static strings — used directly
when they are not bound in the container.
60 changes: 60 additions & 0 deletions docs/9_upgrading_to_2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
title: Upgrading Sirius\Invokator from 1.x to 2.0
---

# Upgrading to 2.0

Version 2.0 modernizes the library for current PHP versions and refreshes the
development tooling. The public API is unchanged, so for most applications the
upgrade is just a version bump. There are two things to be aware of.

## 1. PHP 8.3 or newer is required

The minimum supported PHP version moved from 8.0 to **8.3**. The library is tested
against PHP 8.3, 8.4 and 8.5.

If you are still on PHP 8.0–8.2, stay on the `1.x` releases until you can upgrade
your runtime.

```bash
composer require siriusphp/invokator:^2.0
```

## 2. Value objects are now immutable (`readonly`)

The small value objects used internally now declare their public properties as
`readonly`:

- `ArgumentReference` — created by `arg()`
- `InvokerReference` — created by `ref()`
- `InvokerResult` — created by `result_of()`
- `PipelinePromise`
- `SuggestedResume`
- `SuggestedRetry`

In practice you create these through the helper functions and read their
properties, so nothing changes. The only breaking case is code that **reassigned**
one of their public properties after construction:

```php
$ref = arg(0);
$ref->reference = 1; // 2.0: Error — cannot modify readonly property
```

Build a new instance instead of mutating an existing one:

```php
$ref = arg(1);
```

## Tooling changes (development only)

These do not affect applications consuming the library; they matter only if you
work on the library itself or copy its setup:

- PHPUnit `9` → `12` (test configuration migrated to the 12.x schema).
- PHPStan `1` → `2` (still analysed at level 9).
- [Rector](https://getrector.com/) `2` added; run `composer run rector`.
- `friendsofphp/php-cs-fixer` moved from `tools/` into `require-dev`; `composer run
csfix` now applies the `@PSR12` ruleset.
- The bundled Docker image is based on `php:8.3-fpm`.
Loading
Loading