chevere/action

Action Design Pattern implementation for PHP

2.0.0 2025-09-12 21:29 UTC

This package is auto-updated.

Last update: 2025-09-13 21:37:24 UTC


README

Chevere

Build Code size Apache-2.0 PHPStan Mutation testing badge

Quality Gate Status Maintainability Rating Reliability Rating Security Rating Coverage Technical Debt CodeFactor

Summary

Action implements the Action Design Pattern (a variant of the Command Pattern) that encapsulates operations as reusable, self-validating objects. Built on the Parameter library, it provides a robust framework for defining business logic with strict input/output validation, promoting type safety and reducing boilerplate code across your application.

Installing

Action is available through Packagist and the repository source is at chevere/action.

composer require chevere/action

Quick start

To create an Action class implement the ActionInterface either with use ActionTrait or by extends Action.

Use ActionTrait

To create an action by using ActionTrait:

use Chevere\Action\Interfaces\ActionInterface;
use Chevere\Action\Traits\ActionTrait;

class MyAction implements ActionInterface
{
    use ActionTrait;
    // ...
}

Extends Action

To create an Action by extending Action:

use Chevere\Action\Action;

class MyAction extends Action
{
    // ...
}

Invoke method

Use the __invoke() method to determine action main logic. Use attributes from chevere/parameter on both parameters and return to add assertion rules.

  • Before assertion rules:
class MyAction
{
    public function __invoke(
        string $value
    ): int
    {
        return mb_strlen($value) * 5;
    }
}
  • After assertion rules:
use Chevere\Action\Action;
use Chevere\Parameter\Attributes\IntAttr;
use Chevere\Parameter\Attributes\ReturnAttr;
use Chevere\Parameter\Attributes\StringAttr;

class MyAction extends Action
{
    #[ReturnAttr(
        new IntAttr(min: 0, max: 100)
    )]
    public function __invoke(
        #[StringAttr('/^ok/')]
        string $value
    ): int {
        $this->assertArguments($value);
        return $this->assertReturn(
            mb_strlen($value) * 5
        );
    }
}

The code above demonstrates how to create an Action class with input validation and output assertion. The $value argument must match the regular expression /^ok/ and the return value must be an integer between 0 and 100. See Advanced use for alternative approaches.

Using Action

Invoking Actions

Invoke action's __invoke() method, same as a function. Action internal runtime will assert arguments and return against your expectations.

💡 You can try by running php demo/demo.php

$action = new MyAction();
$result = $action->__invoke('ok muy bueno');
$result = $action('ok muy bueno'); // same thing

ActionName

Actions can use the public function setUp(...) method to define logic which must be executed before the Action is invoked.

For example, an Action may define to require setup location and code arguments:

use Chevere\Action\Action;

class Redirect extends Action
{
    public function setUp(string $location, int $code): void
    {
        $this->location = $location;
        $this->code = $code;
    }
}

With ActionName you can store the Action name and its setUp() arguments:

use Chevere\Action\ActionName;

$actionName = new ActionName(Redirect::class, $location, $code);

Which you can later use directly:

$className = (string) $actionName;
$action = new $className();
$action->setUp(...$actionName->arguments());

Or craft your own ActionName accessors directly on your Actions:

use Chevere\Action\Action;
use Chevere\Action\ActionName;
use Chevere\Action\Interfaces\ActionNameInterface;

class Redirect extends Action
{
    // setUp(...)

    public static function with(string $location, int $code): ActionNameInterface
    {
        return new ActionName(static::class, ...get_defined_vars());
    }
}

Which you can later use like this:

$redirect = Redirect::with('some/location', 302);

Advanced use

This library offers flexible validation strategies to match your application's architecture. While embedding assertions within the __invoke() method provides maximum portability, you can also implement centralized validation logic or delegate validation responsibilities to callers. The following methods enable fine-grained control over where and how validations are performed across.

Accept return method

Use method acceptReturn() to define return value assertion rules. In this context you can use and remix any Parameter function.

Note: Attribute notation #[ReturnAttr] has greater precedence than acceptReturn().

use Chevere\Action\Interfaces\ParameterInterface;
use function Chevere\Parameter\string;

public static function acceptReturn(): ParameterInterface
{
    return string();
}

Assert arguments method

Use method assertArguments() to assert Action's __invoke() arguments against your expectations. When assertArguments() method is called without arguments, it will magic take the arguments from the function caller context.

// magic
$action->assertArguments();
// explicit
$action->assertArguments(...$args);
// explicit, all defined vars
$action->assertArguments(...get_defined_vars());

All the following sample definitions are equivalent in results and will evaluate the same arguments.

public function __invoke($foo, $bar): void {
    $this->assertArguments();
}

public function __invoke($foo, $bar): void {
    $this->assertArguments($foo, $bar);
}

public function __invoke($foo, $bar): void {
    $this->assertArguments(...get_defined_vars());
}

Assert return method

Use method assertReturn() to assert Action's __invoke() return value against your expectations.

$action->assertReturn($result);

Assert method

Use method assert() to assert runtime rules coherence.

$action->assert();

Reflection method

Use method reflection() to access ReflectionAction instance. It enables to read Action's parameters and return assertion rules.

$action::reflection()->parameters();
$action::reflection()->return();

Accept rules static method

This method is called before assert*() calls.

Use method acceptRulesStatic() to define additional static assertion rules to constrain your custom Action design. The purpose of this method is demonstrated at the Controller class to constrain the design by restricting __invoke() parameters to type string.

This is very flexible and allows you to enforce advanced design rules at the class level and for very specific cases, for example to forbid certain parameter names:

public static function acceptRulesStatic(): void {
    if(static::reflection()->parameters()->has('lucho')) {
        throw new LogicException('Parameter $lucho is forbidden');
    }
}

Accept rules runtime method

This method is called before assert*() calls.

Use method acceptRulesRuntime() to define additional runtime assertion rules. The purpose of this method is demonstrated at the HTTP Controller to constrain the design by ensuring the existence of HTTP participants.

public function acceptRulesRuntime(): void
{
    if (! isset($this->_query, $this->_bodyParsed, $this->_files)) {
        throw new LogicException('Server request not set.');
    }
}

Controller

The Controller is a special type of Action in charge of handling command instructions. Its __invoke() method only takes parameters of type string.

Defining a Controller

A Controller implements the ControllerInterface. You can extend Controller to quick create a compliant Controller:

use Chevere\Controller\Controller;

class SomeController extends Controller
{
    // ...
}

Invoke parameters

Parameters are defined in the __invoke() method but it just takes strings.

public function __invoke(
    string $pepito,
    string $paysTwice
): array
{
    // ...
}

Documentation

Documentation is available at chevere.org.

License

Copyright Rodolfo Berrios A.

Chevere is licensed under the Apache License, Version 2.0. See LICENSE for the full license text.

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.