chevere / action
Type-safe invokable objects with attribute-based parameter and return value validation
Installs: 3 928
Dependents: 6
Suggesters: 0
Security: 0
Stars: 3
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/chevere/action
Requires
- php: ^8.1
- chevere/message: ^1.0.0
- chevere/parameter: 2.0.0
Requires (Dev)
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^9.5
- symplify/easy-coding-standard: ^12.1
README
Summary
Chevere Action is a PHP library that encapsulates application operations as reusable, self-validating objects. Built on the Parameter library, it implements the Action Design Pattern to enforce strict input/output validation at the class level, reducing runtime errors and improving code reliability with minimal boilerplate.
Installing
Action is available through Packagist and the repository source is at chevere/action.
composer require chevere/action
Quick Start
Creating an Action
Create an Action by implementing the ActionInterface either by using ActionTrait or extending the Action class:
Using ActionTrait
use Chevere\Action\Interfaces\ActionInterface; use Chevere\Action\Traits\ActionTrait; class GetUserAction implements ActionInterface { use ActionTrait; public function __invoke(int $userId): array { return ['id' => $userId, 'name' => 'John']; } }
Extending Action
use Chevere\Action\Action; class GetUserAction extends Action { public function __invoke(int $userId): array { return ['id' => $userId, 'name' => 'John']; } }
Adding Validation
Enhance Actions with input and output validation using attributes from the Parameter library:
use Chevere\Action\Action; use Chevere\Parameter\Attributes\_arrayp; use Chevere\Parameter\Attributes\_int; use Chevere\Parameter\Attributes\_string; use Chevere\Parameter\Attributes\_return; class GetUserAction extends Action { #[_return( new _arrayp( userId: new _int(min: 1), name: new _string(), ) )] public function __invoke( #[_int(min: 1)] int $userId ): array { $this->assertArguments($userId); $result = ['id' => $userId, 'name' => 'John']; return $this->assertReturn($result); } }
Invoking an Action
Invoke an Action as you would a function:
$action = new GetUserAction(); $result = $action(1); // or $action->__invoke(1)
Advanced Usage
Parameter Validation Methods
Define validation rules using dedicated methods for more control and flexibility. This is especially useful when validation rules cannot be expressed as attribute attributes (literal values only).
Using acceptParameters()
Define input validation rules centrally:
use Chevere\Action\Action; use Chevere\Parameter\Interfaces\ParametersInterface; use function Chevere\Parameter\parameters; use function Chevere\Parameter\string; use function Chevere\Parameter\int; class CreatePostAction extends Action { public function __invoke( string $title, string $content, int $authorId ): array { $this->assertArguments($title, $content, $authorId); return ['id' => 1]; } public static function acceptParameters(): ParametersInterface { return parameters( title: string(minLength: 5, maxLength: 200), content: string(minLength: 10), authorId: int(min: 1), ); } }
Using acceptReturn()
Define output validation rules:
use Chevere\Action\Action; use Chevere\Parameter\Interfaces\ParameterInterface; use function Chevere\Parameter\arrayOf; use function Chevere\Parameter\int; class GetUserAction extends Action { public function __invoke(int $userId): array { $this->assertArguments($userId); $result = ['id' => $userId, 'name' => 'John']; return $this->assertReturn($result); } public static function acceptReturn(): ParameterInterface { return arrayOf( int(key: 'id', min: 1), string(key: 'name', minLength: 1), ); } }
Assertion Methods
Control exactly when and how validations occur:
assertArguments()
Validate input against defined rules. Can be called with explicit arguments or automatically from the caller context:
// Automatic extraction from caller context public function __invoke($foo, $bar) { $this->assertArguments(); } // Explicit arguments public function __invoke($foo, $bar) { $this->assertArguments($foo, $bar); } // Using get_defined_vars() public function __invoke($foo, $bar) { $this->assertArguments(...get_defined_vars()); }
assertReturn()
Validate the return value against defined rules:
public function __invoke(int $id): array { $result = $this->fetchUser($id); return $this->assertReturn($result); }
assert()
Validate runtime rule coherence. Useful for checking internal state:
public function __invoke(): void { $this->setupDependencies(); $this->assert(); }
Advanced Rules
Static Rules with acceptRulesStatic()
Enforce design rules at class definition time. Called before any assertions:
public static function acceptRulesStatic(): void { $reflection = static::reflection(); // Enforce that parameter $password is never used if ($reflection->parameters()->has('password')) { throw new LogicException('Password parameter not allowed'); } }
Runtime Rules with acceptRulesRuntime()
Enforce rules based on instance state. Called before assertions at invocation time:
private array $config = []; public function acceptRulesRuntime(): void { if (empty($this->config)) { throw new RuntimeException('Configuration required before invocation'); } }
Action Setup Method
Use setUp() to initialize an Action before invocation:
class SendEmailAction extends Action { private SmtpConfig $smtpConfig; public function setUp(SmtpConfig $config): void { $this->smtpConfig = $config; } public function __invoke(string $email): bool { $this->acceptRulesRuntime(); return mail($email, 'Hello', 'World'); } public function acceptRulesRuntime(): void { if (!isset($this->smtpConfig)) { throw new RuntimeException('SMTP configuration required'); } } } // Usage $action = new SendEmailAction(); $action->setUp($smtpConfig); $result = $action('user@example.com');
ActionName
Store and manage Action instances with their setup arguments using ActionName:
use Chevere\Action\ActionName; $actionName = new ActionName( SendEmailAction::class, $smtpConfig ); // Later: reconstruct and invoke $className = (string) $actionName; $action = new $className(); $action->setUp(...$actionName->arguments()); $action('user@example.com');
Or use a factory method on your Action:
class SendEmailAction extends Action { public static function configure(SmtpConfig $config): ActionNameInterface { return new ActionName(static::class, $config); } } // Usage $configured = SendEmailAction::configure($smtpConfig); $action = new (string)$configured(); $action->setUp(...$configured->arguments());
Reflection Access
Access Action metadata using reflection():
$action = new MyAction(); // Get parameter definitions $parameters = $action::reflection()->parameters(); // Get return definition $return = $action::reflection()->return(); // Check if parameter exists if ($parameters->has('userId')) { // ... } // Iterate parameters foreach ($parameters as $name => $parameter) { echo $name . ': ' . get_class($parameter); }
The Controller is a specialized Action for handling command-like instructions. Unlike standard Actions that accept any type of parameters, Controllers restrict parameters to scalar types: string, int, and float.
Controllers are ideal for handling user input from APIs, CLI commands, or form submissions where all arguments arrive as scalar values.
Creating a Controller
Extend the Controller class to create a compliant Controller:
use Chevere\Action\Controller; use Chevere\Parameter\Interfaces\ParametersInterface; use function Chevere\Parameter\parameters; use function Chevere\Parameter\string; use function Chevere\Parameter\int; class CreatePostController extends Controller { public function __invoke( string $title, string $content, int $authorId ): array { $this->assertArguments($title, $content, $authorId); return [ 'id' => 1, 'title' => $title, 'author' => $authorId ]; } public static function acceptParameters(): ParametersInterface { return parameters( title: string(minLength: 5, maxLength: 200), content: string(minLength: 10), authorId: int(min: 1), ); } }
Usage Example
$controller = new CreatePostController(); $result = $controller( title: 'My First Post', content: 'This is great content.', authorId: 42 ); // Returns: ['id' => 1, 'title' => 'My First Post', 'author' => 42]
Note: Controllers enforce type validation at the class level. Any parameter with a type other than string|int|float will trigger a validation error during class initialization.
Documentation
Documentation is available at chevere.org/packages/action.
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.