rougin / weasley
Helpers and utilities for Slytherin.
Requires
- php: >=5.3.0
- rougin/slytherin: ~0.9
- vlucas/valitron: ~1.4
Requires (Dev)
- phpunit/phpunit: ~4.2|~5.7|~6.0|~7.0|~8.0|~9.0
- sanmai/phpunit-legacy-adapter: ~6.1|~8.0
- symfony/console: ~2.8|~3.0|~4.0|~5.0|~6.0
README
Weasley is a PHP package that provides generators, helpers, and utility classes for the Slytherin. Its goal is to improve the overall productivity when writing web applications based on Slytherin by reducing in writing code related to CRUD operations.
Installation
Install Weasley through Composer:
$ composer require rougin/weasley
Basic usage
Weasley is built on a layered architecture where each domain delegates to a dedicated package:
| Package | Purpose |
|---|---|
| Slytherin | Simple, extensible PHP micro-framework. |
| Onion | HTTP middlewares for Slytherin. |
| Valla | A simple validation package in PHP. |
| Blueprint | A bootstrap for PHP console projects. |
| Classidy | Create PHP classes using PHP. |
Code generators
Weasley provides commands that generate code for Slytherin components. These commands allow Slytherin to be a rapid prototyping tool when creating web-based applications.
Use the weasley command to access the list of its available commands:
$ vendor/bin/weasley
Available commands
| Command | Description |
|---|---|
make:check |
Creates a new validation (Check) class |
make:handler |
Creates a new HTTP Middleware class |
make:package |
Creates a new Slytherin Integration class |
make:route |
Creates a new HTTP route class |
Each command accepts the following options:
$ vendor/bin/weasley make:check UserCheck --path src/Checks --namespace App\Checks --author "John Doe"
| Option | Default | Description |
|---|---|---|
--path |
src/Checks |
Directory where the file will be created |
--namespace |
App\Checks |
Namespace for the generated class |
--author |
(empty) | Author name in the class docblock |
HTTP routes
Weasley provides classes for creating HTTP routes in the RESTful style. In other PHP frameworks, these are also known as Controllers.
HttpRoute
A simple HTTP route class that provides a json() helper for returning JSON responses:
use Rougin\Weasley\Route; class Welcome extends Route { public function index() { $data = array('message' => 'Hello world!'); return $this->json($data); } }
The Route class is a root-namespace alias for Rougin\Weasley\Routes\HttpRoute. It accepts the PSR-07 request and response through its constructor:
/** @var \Psr\Http\Message\ServerRequestInterface */ $request = /** ... */; /** @var \Psr\Http\Message\ResponseInterface */ $response = /** ... */; $route = new Welcome($request, $response); /** @var \Psr\Http\Message\ResponseInterface */ $result = $route->index();
JsonRoute
Extends HttpRoute with built-in CRUD operations backed by an Eloquent model and a validator:
use Rougin\Weasley\Routes\JsonRoute; class UsersRoute extends JsonRoute { protected $model = 'Acme\Models\User'; protected $mutator = 'Rougin\Weasley\Mutators\RestMutator'; protected $validator = 'Acme\Checks\UserCheck'; }
Once defined, the following methods become available:
| Method | HTTP Equivalent | Description |
|---|---|---|
index() |
GET /users |
Returns all records (paginated when illuminate/pagination is installed) |
show($id) |
GET /users/{id} |
Returns a single record |
store() |
POST /users |
Creates a new record from the parsed request body |
update($id) |
PUT /users/{id} |
Updates an existing record |
delete($id) |
DELETE /users/{id} |
Deletes a record |
The $model must be an Eloquent model with $fillable defined. The $validator must be a Check subclass. Both are validated at construction time and will throw UnexpectedValueException if missing.
HTTP handlers
Weasley provides HTTP middlewares (handlers) that process incoming requests and outgoing responses. Each handler implements Rougin\Slytherin\Middleware\MiddlewareInterface.
Note
Starting v0.8, all handler classes are thin wrappers over Onion (rougin/onion). Every feature described below originates from Onion's classes.
AllowCrossOrigin
Adds CORS headers to every response:
use Rougin\Weasley\Handlers\AllowCrossOrigin; // Default: allows all origins, allows GET/POST/PUT/DELETE/OPTIONS $cors = new AllowCrossOrigin; // Restrict to specific origins and methods via constructor $cors = new AllowCrossOrigin( array('https://example.com', 'https://api.example.com'), array('GET', 'POST') ); // Or configure fluently $cors = (new AllowCrossOrigin) ->allowed(array('https://example.com')) ->methods(array('GET', 'POST', 'DELETE'));
Note
If the incoming request method is OPTIONS, the handler returns an empty response immediately (preflight support).
EmptyStringToNull
Converts empty, "null", and "undefined" string values from query parameters and parsed body to actual null:
use Rougin\Weasley\Handlers\EmptyStringToNull; $handler = new EmptyStringToNull; // query params: ?age=&name=null&role=undefined // becomes: ['age' => null, 'name' => null, 'role' => null]
JsonContentType
Sets the Content-Type header to application/json on every response that does not already have one:
use Rougin\Weasley\Handlers\JsonContentType; $handler = new JsonContentType; // Response header: Content-Type: application/json
MutateRequest
An extensible base class for transforming request values. Extend it and override the transform() method:
use Rougin\Weasley\Handlers\MutateRequest; class SanitizeHtml extends MutateRequest { protected function transform($value) { return is_string($value) ? strip_tags($value) : $value; } }
The transform() method is called recursively on every value in query parameters and the parsed body. Arrays are recursed into automatically.
SpoofHttpMethod
Replaces the HTTP verb of the request with the value of a configurable key from the parsed body, enabling HTML forms to simulate PATCH, PUT, or DELETE:
use Rougin\Weasley\Handlers\SpoofHttpMethod; // Default key is "_method" $spoof = new SpoofHttpMethod; // Use a custom key $spoof = new SpoofHttpMethod('_action'); // or fluently: $spoof->key('_action');
When the request body contains ['_method' => 'PATCH'], the request method becomes PATCH.
TrimStringValue
Trims whitespace from all string values in query parameters and the parsed body:
use Rougin\Weasley\Handlers\TrimStringValue; $handler = new TrimStringValue; // query params: ?name= Rougin // becomes: ['name' => 'Rougin']
Validation
Weasley provides the Check class for validating data. It is built on Valla (rougin/valla) and supports all rules from Valla's rule engine (e.g., required, email, and more):
$check = new UserCheck; $data = /* e.g., from request */; if ($check->valid($data)) { // Data passed validation } else { // Get all errors: array<string, string[]> $errors = $check->errors(); // Get the first error only echo $check->firstError(); // e.g., "Age is required" }
Error messages are built from the label and the rule description. For example, a missing email field with the label "Email" produces "Email is required".
Method-based style (recommended)
The method-based style is recommended when rules depend on the submitted data (e.g., conditionally requiring a field):
use Rougin\Weasley\Check; class UserCheck extends Check { /** * @return array<string, string> */ public function labels() { return array( 'name' => 'Name', 'email' => 'Email', 'age' => 'Age', ); } /** * @param array<string, mixed> $data * * @return array<string, string> */ public function rules(array $data) { $rules = array( 'name' => 'required', 'email' => 'required|email', 'age' => 'required|numeric|min:18', ); return $rules; } }
Property-based style (legacy)
This can also define rules and labels as protected properties for simple, static rule sets:
use Rougin\Weasley\Check; class SimpleCheck extends Check { protected $labels = array( 'name' => 'Name', 'email' => 'Email', ); protected $rules = array( 'name' => 'required', 'email' => 'required|email', ); }
Mutators
Mutators transform data into a specified output format (e.g., PSR-07 responses, API payloads). They implement Rougin\Weasley\Contract\Mutator.
JsonMutator
Encodes data as JSON and returns a PSR-07 response:
use Rougin\Weasley\Mutators\JsonMutator; $mutator = new JsonMutator($response); $result = $mutator->mutate(array('status' => 'success')); // Content-Type: application/json // Body: {"status":"success"}
A second argument accepts JSON encoding options:
$mutator = new JsonMutator($response, JSON_PRETTY_PRINT);
RestMutator
Formats paginated results following PayPal's API Style Guide:
use Rougin\Weasley\Mutators\RestMutator; $mutator = new RestMutator; $result = $mutator->mutate($paginator); // $result becomes: // [ // 'total_items' => 100, // 'total_pages' => 10, // 'items' => [...], // ]
When the data is not a LengthAwarePaginator, the mutator wraps it as ['items' => $data].
Third-party packages
Weasley provides IntegrationInterface implementations for wiring third-party packages into Slytherin's container.
Laravel\Eloquent
Enables Eloquent ORM from Laravel:
$ composer require illuminate/database
use Rougin\Slytherin\Container\Container; use Rougin\Slytherin\Integration\Configuration; use Rougin\Weasley\Packages\Laravel\Eloquent; $config = new Configuration; $config->set('database.default', 'sqlite'); $config->set('database.sqlite.driver', 'sqlite'); $config->set('database.sqlite.database', ':memory:'); $container = new Container; (new Eloquent)->define($container, $config); // Eloquent models are now available globally $users = User::all();
Laravel\Blade
Enables Blade templating:
$ composer require illuminate/view
use Rougin\Slytherin\Container\Container; use Rougin\Slytherin\Integration\Configuration; use Rougin\Weasley\Packages\Laravel\Blade; $config = new Configuration; $config->set('illuminate.view.templates', __DIR__ . '/templates'); $config->set('illuminate.view.compiled', __DIR__ . '/cache'); $container = new Container; (new Blade)->define($container, $config); // Resolve the renderer from the container $renderer = $container->get('Rougin\Weasley\Renderers\BladeRenderer'); echo $renderer->render('welcome', array('name' => 'Rougin'));
Laravel\Paginate
Adds pagination support to Eloquent models:
$ composer require illuminate/pagination
use Rougin\Slytherin\Container\Container; use Rougin\Slytherin\Integration\Configuration; use Rougin\Weasley\Packages\Laravel\Paginate; // Assuming Eloquent is already booted (see above) $container = new Container; (new Paginate)->define($container, new Configuration); // Eloquent models now have a paginate() method $paginator = User::paginate(10); // 10 per page echo $paginator->total(); // total records echo $paginator->currentPage(); // current page number
Session
Provides session management through SessionHandlerInterface:
use Rougin\Slytherin\Container\Container; use Rougin\Slytherin\Integration\Configuration; use Rougin\Weasley\Packages\Session; $config = new Configuration; $config->set('session.cookies', array()); $config->set('session.expiration', 3600); $config->set('session.path', __DIR__ . '/sessions'); $container = new Container; (new Session)->define($container, $config); // Resolve the session from the container --- $class = 'Rougin\Weasley\Contract\Session'; $session = $container->get($class); // ------------------------------------------ $session->set('user_id', 42); echo $session->get('user_id'); // 42 $session->regenerate(); // rotate session ID $session->delete('user_id'); // remove a key
Contracts
Weasley defines interfaces for its extensible components:
Contract\Mutator
namespace Rougin\Weasley\Contract; interface Mutator { /** * @param mixed $data * @return mixed */ public function mutate($data); }
Implement this interface to create custom mutators. JsonMutator and RestMutator are built-in implementations.
Contract\Session
namespace Rougin\Weasley\Contract; interface Session { /** * @param string $key * @param mixed $default * @return mixed */ public function get($key, $default = null); /** * @param string $key * @param mixed $value * @return self */ public function set($key, $value); /** * @param string $key * @return boolean */ public function delete($key); /** * @param boolean $delete * @return boolean */ public function regenerate($delete = false); }
Utilities
Random
Generates cryptographically secure random strings:
use Rougin\Weasley\Assorted\Random; $token = Random::make(32); // e.g., "a7f3b9c2d1e8..."
Changelog
Please see CHANGELOG for more recent changes.
Upgrade Guide
As Weasley continues to evolve, there might be some breaking changes in its internal code during development. The said changes can be found in UPGRADING.
Contributing
See CONTRIBUTING on how to contribute.
License
The MIT License (MIT). Please see LICENSE for more information.