chevere / action
Action Design Pattern implementation for PHP
Installs: 3 013
Dependents: 6
Suggesters: 0
Security: 0
Stars: 3
Watchers: 0
Forks: 0
Open Issues: 0
Requires
- php: ^8.1
- chevere/message: ^1.0.0
- chevere/parameter: ^1.2.0
Requires (Dev)
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^9.5
- symplify/easy-coding-standard: ^12.1
- xrdebug/php: ^3.0
README
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.