Provides utilities for rapid prototyping Slytherin projects.
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.
Install Weasley through Composer:
$ composer require rougin/weasleyWeasley 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. |
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| 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 |
Weasley provides classes for creating HTTP routes in the RESTful style. In other PHP frameworks, these are also known as Controllers.
HttpRouteA 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();JsonRouteExtends 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.
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.
AllowCrossOriginAdds 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).
EmptyStringToNullConverts 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]JsonContentTypeSets 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/jsonMutateRequestAn 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.
SpoofHttpMethodReplaces 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.
TrimStringValueTrims 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']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".
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;
}
}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 transform data into a specified output format (e.g., PSR-07 responses, API payloads). They implement Rougin\Weasley\Contract\Mutator.
JsonMutatorEncodes 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);RestMutatorFormats 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].
Weasley provides IntegrationInterface implementations for wiring third-party packages into Slytherin's container.
Laravel\EloquentEnables Eloquent ORM from Laravel:
$ composer require illuminate/databaseuse 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\BladeEnables Blade templating:
$ composer require illuminate/viewuse 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\PaginateAdds pagination support to Eloquent models:
$ composer require illuminate/paginationuse 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 numberSessionProvides 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 keyWeasley defines interfaces for its extensible components:
Contract\Mutatornamespace 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\Sessionnamespace 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);
}RandomGenerates cryptographically secure random strings:
use Rougin\Weasley\Assorted\Random;
$token = Random::make(32); // e.g., "a7f3b9c2d1e8..."