Uses SOLID principles in writing web-based applications.
Slytherin is a simple and extensible PHP micro-framework that tries to achieve a SOLID-based design for creating your next web application. It uses Composer as the dependency package manager to add, update or even remove external packages.
In the current state of PHP ecosystem, the mostly used PHP frameworks are Symfony and Laravel which provide great tools for every PHP software engineer. While the said PHP frameworks provide a kitchen-sink solution for every need (e.g., content management system (CMS), CRUD, etc.), they are often overkill, overwhelming at first, or sometimes uses a strict directory structure.
With this, Slytherin tries an alternative approach to only require the basic tools (e.g., HTTP and Routing) and let the application evolve from a simple API tool to a full-featured web application. With no defined directory structure, Slytherin can be used to mix and match any structure based on the application's requirements and to tap more of the open-source packages in the PHP ecosystem.
As Slytherin is evolving as a micro-framework, there might be some breaking changes in its internal code. The said changes can be found in the Upgrade Guide page.
Slytherin is inspired by the following packages below and their respective implementations. Their contributions improved my understanding of writing frameworks and application logic from scratch:
Kindly check the The First Hello World page in this wiki for the sample code.
To implement a simple Hello World application with Slytherin, kindly create a new directory first for the tutorial project (e.g., Hogwarts):
$ mkdir HogwartsThen open the newly created directory (e.g., Hogwarts) and install the Slytherin package using Composer:
$ cd Hogwarts
$ composer require rougin/slytherinNote
This step requires Composer to be installed on the local machine. Kindly see the instructions in its Download page for its installation:
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === 'dac665fdc30fdd8ec78b38b9800061b4150413ff2e3b6f88543c636f7cd84f6db9189d43a81e5503cda447da73c7e5b6') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"After installing the Slytherin package in the current directory, create a directory named app/web then a PHP file named index.php:
$ mkdir app
$ cd app
$ mkdir web
$ cd web
$ touch index.phpFrom the index.php file, copy the following code below for showing the Hello world! text in the web browser:
// app/web/index.php
use Rougin\Slytherin\Application;
// Load the Composer autoloader ----
$root = dirname(dirname(__DIR__));
require "$root/vendor/autoload.php";
// ---------------------------------
// Initialize the Slytherin application ---
$app = new Application;
// ----------------------------------------
// Define an HTTP route ---
$app->get('/', function ()
{
return 'Hello world!';
});
// ------------------------
// Run the Slytherin application ---
echo $app->run();
// ---------------------------------Once the code is pasted in the index.php file, execute the code below to the Terminal to start the PHP built-in web server:
$ php -S localhost:8000 -t app/webLastly, open a new tab in the current web browser then access the link below. This should show the text Hello world! from the web browser:
http://localhost:8000Based on the previous guide, the sample code provided a definition of an HTTP route:
// app/web/index.php
// ...
// Define an HTTP route ---
$app->get('/', function ()
{
return 'Hello world!';
});
// ------------------------
// ...From the context in web development, an HTTP route defines which action to run from the incoming URL:
──► START
──► User requests "/" in the URL
──► Request received by Slytherin
──► Slytherin checks if the "/" request URL is a valid route
──► Returns the response "Hello world!" to the user
──► ENDNote
In the application life cycle of Slytherin, the incoming request shall always return its corresponding response.
To create a simple HTTP route, kindly specify the URL that needs to be provided from the request first then the action that will be run if the request URL matched:
$app->{METHOD}($url, $action);Note
The {METHOD} can be replaced with the available HTTP methods below currently being supported by Slytherin. The said HTTP methods can be found below in the next section.
// app/web/index.php
// ...
$app->get('/hello', function ()
{
return 'Hello!';
});
// ...From the web browser, open the link below and it should return the text Hello!:
http://localhost:8000/helloSlytherin also supports dynamic route parameters by adding a parameter with curly braces ({}) in the URL. To access the said parameter, it must also be available in the route action:
// app/web/index.php
// ...
$app->get('/hi/{name}', function ($name)
{
return 'Hello ' . $name . '!';
});
// ...Then open the link below from a web browser and it should return the text Hello royce!:
http://localhost:8000/hi/royceIn one of the provided examples above, the application defines an HTTP route of GET with an URL of "/hello". The keyword GET is one of the available HTTP methods that can be used for retrieving from and sending data to a server:
| HTTP Method | Intended Action or Description |
|---|---|
| GET | Used for retrieving data from the server. |
| POST | Used for sending data to the server and creates a new resource. |
| PUT | Can be used to update an existing resource. |
| PATCH | Same as PUT, as it also modifies an existing resource. |
| DELETE | Used for deleting a specified resource. |
To specify a different HTTP method (e.g., POST), it is simple as defining its HTTP route in the code:
// app/web/index.php
// ...
$app->delete('/users/{id}', function ($id)
{
return 'Deletes the specified user.';
});
$app->get('/users', function ()
{
return 'Shows list of users.';
});
$app->get('/users/{id}', function ($id)
{
return 'Shows the specified user.';
});
$app->post('/users', function ($id)
{
return 'Creates a new user.';
});
$app->put('/users/{id}', function ($id)
{
return 'Updates the specified user.';
});
// ...Note
As other HTTP methods besides GET cannot be accessed directly from a web browser, a tool such as Postman can be used in these situations.
Previous code examples only provide route actions as anonymous functions. But Slytherin can also use simple PHP classes as its route action to improve code readability and maintainability:
// app/web/index.php
// ...
class Hallo
{
public function greet()
{
return 'Hallo!';
}
}
$app->get('/hallo', 'Hallo@greet');
// ...Defining HTTP routes with PHP classes can also be defined as an array:
// app/web/index.php
// ...
$app->get('/hallo', [Hallo::class, 'greet']);
// ...Slytherin supports resolution of parameters in an HTTP route action without the need of instantiation of one of its specified parameters (e.g., new Hello). To automagically create class instances, kindly specify the name of the class to its respective argument:
// app/web/index.php
// ...
class Hello
{
public function greet($name)
{
return 'Hello ' . $name . '!';
}
}
/**
* The $hello argument will be instantiated with the
* Hello class, without need to code "new Hello" to it.
*/
$app->get('/hi/{name}', function ($name, Hello $hello)
{
return $hello->greet($name);
});
// ...Then try to open the link below from a web browser and it should return the text Hello royce!:
http://localhost:8000/hi/royceResolving dependencies only works automagically on PHP classes with simple dependencies like the sample code below:
class Greet
{
public function hi($name)
{
return 'Hi ' . $name . '!';
}
}
/**
* This is a simple class as it can be
* defined manually simply below:
*
* $hello = new Hello(new Greet);
*/
class Hello
{
protected $greet;
public function __construct(Greet $greet)
{
$this->greet = $greet;
}
public function hi($name)
{
return $this->greet->hi($name);
}
}However, if one of the PHP classes contains arguments that need to be specified (e.g., arguments without class type-hints, etc.), the specified PHP class must be defined in a Container then pass it to the Application class:
// app/web/index.php
use Rougin\Slytherin\Application;
use Rougin\Slytherin\Container\Container;
// ...
/**
* This will be instantiated by Slytherin but
* the $text variable will return empty.
*/
class Greet
{
protected $text;
public function __construct($text)
{
$this->text = $text;
}
public function hi($name)
{
return $this->text . ' ' . $name . '!';
}
}
// ...
// Initialize the container ---
$container = new Container;
// ----------------------------
// Add the "Greet" class to the container --------
$container->set(Greet::class, new Greet('hello'));
// -----------------------------------------------
// Add the container to the application ---
$app = new Application($container);
// ----------------------------------------
// ...To check if greeting a name with hello works, open the link below from a web browser and it should return the text hello rougin!:
http://localhost:8000/hi/rouginNote
It is best practice to specify complex classes to the Container class prior running the Application class.
PHP Standard Recommendations (PSRs) is a specification published by the PHP Framework Interoperability Group (PHP-FIG) and serves the standardization of programming concepts in PHP. Its aim is to enable interoperability of components and to provide a common technical basis for implementation of proven concepts for optimal programming and testing practices.
As Slytherin tries to be compatible and extensible with other PHP packages, its internal code is being written based on the following PSRs:
| Number | Name | Description |
|---|---|---|
| PSR-04 | Autoloading Standard | It describes a specification for autoloading classes from file paths. It is fully interoperable and can be used in addition to any other autoloading specification, including PSR-0. This also describes where to place files that will be auto loaded according to the specification. |
| PSR-07 | HTTP Message Interface | It describes common interfaces for representing HTTP messages as described in RFC 7230 and RFC 7231, and URIs for use with HTTP messages as described in RFC 3986. |
| PSR-11 | Container Interface | It describes a common interface for dependency injection containers. The goal is to standardize how frameworks and libraries make use of a container to obtain objects and parameters. |
| PSR-12 | Extended Coding Style Guide | It extends, expands and replaces PSR-02, the coding style guide and requires adherence to PSR-01, the basic coding standard. |
| PSR-15 | HTTP Server Request Handlers | It describes common interfaces for HTTP server request handlers and HTTP server middleware components that use HTTP messages. |
Using Slytherin, there is no need to instantiate the specified PSR interfaces as they will be created once used inside the HTTP route’s action. With this, just simply add the specified interface (e.g., ServerRequestInterface from PSR-07) inside the specified HTTP route:
// app/web/index.php
use Psr\Http\Message\ServerRequestInterface;
// ...
$app->get('/', function (ServerRequestInterface $request)
{
$query = $request->getQueryParams();
$name = $query['name'] ?? 'world';
return 'Hello ' . $name . '!';
});
// ...From the web browser, open the link below and it should show a text of Hello rougin!:
http://localhost:8000/?name=rouginIf the name query parameter is not specified, it should show a Hello world! text instead.
Note
The code responsible for autoloading the class instances inside the HTTP route are also implemented using the PSR-11 standard.
Since Slytherin is implemented under PSR interfaces, it is now easy to swap PSR implementations. Slytherin also provides its own PSR implementations mentioned above so that it can work directly without outside dependencies. However, it is possible and easy to replace those PSR implementations with a third-party implementation.
For this example, the goal is to replace the ServerRequestInterface of Slytherin. To start, kindly download a third-party package (e.g., httpsoft/http-message) using Composer:
$ composer require httpsoft/http-messageAfter downloading the specified third-party package, proceed to instantiate the ServerRequest class using the code below:
// app/web/index.php
use HttpSoft\Message\ServerRequestFactory;
// ...
// Create the ServerRequest from the third-party package ---------
$factory = new ServerRequestFactory;
$uri = $_SERVER['REQUEST_URI'];
$method = $_SERVER['REQUEST_METHOD'];
$request = $factory->createServerRequest($method, $uri, $_SERVER);
// ---------------------------------------------------------------
// Add the third-party ServerRequest class to the container ---
$container->set(ServerRequestInterface::class, $request);
// ------------------------------------------------------------
// ...
$app->get('/request', function (ServerRequestInterface $request)
{
return get_class($request);
});
// ...Opening the link below from the web browser should return the ServerRequest class name from the third-party package (e.g., HttpSoft\Message\ServerRequest):
http://localhost:8000/requestSlytherin does not provide an implementation regarding the template layer as the PHP language itself is a template engine. Instead, whatever is being returned in an HTTP route, Slytherin converts it into a HTTP response (ResponseInterface) object. Returning HTTP responses from HTTP requests makes Slytherin more flexible in integrating on third-party template engines:
// app/web/index.php
// ...
$app->get('/', function ()
{
return 'Hello <b>world</b>!';
});
// ...Opening the link below should return a text Hello world with the world displayed in bold text:
http://localhost:8000If there is a need to use a template engine, with the purpose to escape values or to separate the presentation layer from its business logic, Slytherin provides a simple templating interface using the RendererInterface that can be used inside the HTTP route.
To initialize the template renderer, kindly create a new file named hello.php in the app/plates directory then paste the following HTML code:
<!-- app/plates/hello.php -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<p>Hello <b><?php echo $name; ?></b>!</p>
</body>
</html>From the main index.php file, initialize the Renderer instance with the path of the templates to be used:
// app/web/index.php
use Rougin\Slytherin\Template\Renderer;
use Rougin\Slytherin\Template\RendererInterface;
// ...
// Initialize the Renderer instance ---
$paths = array($root . '/app/plates');
$renderer = new Renderer($paths);
// ------------------------------------
// Add the Renderer to the container ----------------
$container->set(RendererInterface::class, $renderer);
// --------------------------------------------------
// ...
$app->get('/', function (RendererInterface $view)
{
$data = array('name' => 'Slytherin');
return $view->render('hello', $data);
});
// ...Refreshing the same page from the web browser based on the previous section should return a text Hello Slytherin! with the Slytherin text displayed in bold.
Tip
Check the Template documentation under Components to check alternative implementations for the RendererInterface.
A Middleware is a layer of actions that are wrapped around a piece of core logic in an application. It provides functionality to change an HTTP request or an HTTP response. From the perspective of Slytherin, a middleware may do the following:
$fn = function ($request, $next)
{
// Modify the query parameters from request ---
$data = array('name' => 'Slytherin');
$request = $request->withQueryParams($data);
// --------------------------------------------
// Proceed to the next middleware ---
return $next($request);
// ----------------------------------
};$fn = function ($request, $next)
{
// Returns the last middleware prior this ---
$response = $next($request);
// ------------------------------------------
// Modify the response directly ------
if ($response->getStatusCode() >= 500)
{
// Add sample conditions
}
// -----------------------------------
return $response;
};Prior to creating and adding middlewares to HTTP routes, a middleware dispatcher must be added first to the Container class:
// app/web/index.php
// ...
use Rougin\Slytherin\Middleware\Dispatcher;
use Rougin\Slytherin\Middleware\DispatcherInterface;
// ...
// Add the Middleware Dispatcher to the container ----------
$container->set(DispatcherInterface::class, new Dispatcher);
// ---------------------------------------------------------After defining the middleware dispatcher, a middleware can be added into a HTTP route by adding it after the route action parameter:
// app/web/index.php
// ...
$middleware = function ($request, $next)
{
$newQuery = array('name' => 'middleware');
$request = $request->withQueryParams($newQuery);
return $next->handle($request);
};
$app->get('/hello', function (ServerRequestInterface $request)
{
$query = $request->getQueryParams();
return 'Hello ' . ($query['name'] ?? 'world') . '!';
}, $middleware);
// ...Opening the link below in a web browser should return a text of Hello middleware! even though there is a defined value in the name query parameter since the HTTP request was intercepted inside a middleware:
http://localhost:8000/helloThe previous pages in the Getting Started guide should cover all the features of Slytherin. Once the concepts were familiarized and understood, it is now okay to proceed with preparing the directory structure and its implementation to a new Slytherin application.
Since Slytherin is designed to be extensible and easy to integrate with other PHP packages, there are no defined directory structure nor best practices as each project has its own needs and requirements to be fulfilled. With this, A Slytherin-based project can be implemented but not limited to the following implementations:
| Implementation | Description |
|---|---|
| ContainerInterface\ | Based on the PSR-11 standard and the main core component of the Slytherin. |
| Components\ | The classic implementation of Slytherin prior to the PSR-11 standard. |
| IntegrationInterface\ | Similar to ContainerInterface but it can be used for integrating third-party packages. |
| Application\ | It is a quick implementation for creating Slytherin applications without specifying anything. |
Note
These are the only available implementations that are being currently used in Slytherin. It can be expanded into other implementations and the specified implementations can co-exist with each other.
As Slytherin tries to be extensible as much as possible, there are many ways on how to setup the Application instance. One of them is the ContainerInterface implementation which is the most important implementation for Slytherin.
Below is an example code for creating a Slytherin-based application:
// app/web/index.php
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Rougin\Slytherin\Application;
use Rougin\Slytherin\Container\Container;
use Rougin\Slytherin\Http\Response;
use Rougin\Slytherin\Http\ServerRequest;
use Rougin\Slytherin\Routing\Dispatcher;
use Rougin\Slytherin\Routing\DispatcherInterface;
use Rougin\Slytherin\Routing\Router;
// Load the Composer autoloader ----
$root = dirname(dirname(__DIR__));
require "$root/vendor/autoload.php";
// ---------------------------------
// Define HTTP objects that is compliant to PSR-07 standards ---
$request = new ServerRequest((array) $_SERVER);
$response = new Response(http_response_code());
// -------------------------------------------------------------
// Create a new router to add an HTTP route... ---
$router = new Router;
$router->get('/', function ()
{
return 'Hello world!';
});
// -----------------------------------------------
// ...then define it to a dispatcher ---
$dispatcher = new Dispatcher($router);
// -------------------------------------
// Add the above objects through a container ------------
$container = new Container;
$container->set(ServerRequestInterface::class, $request);
$container->set(ResponseInterface::class, $response);
$container->set(DispatcherInterface::class, $dispatcher);
// ------------------------------------------------------
// Lastly, run the application ------
(new Application($container))->run();
// ----------------------------------Then kindly check the Running the web server section on how to run the example code above.
ContainerInterfaceBased on the example code above, the $container must be implemented in Rougin\Slytherin\Container\ContainerInterface:
namespace Rougin\Slytherin\Container;
use Psr\Container\ContainerInterface as PsrContainerInterface;
interface ContainerInterface extends PsrContainerInterface
{
/**
* Sets a new instance to the container.
*
* @param string $id
* @param mixed $concrete
* @return self
*/
public function set($id, $concrete);
}Kindly notice that the said interface is extended to the ContainerInterface of the official PSR-11 package (psr/container) which is a common interface for dependency injection containers:
namespace Psr\Container;
interface ContainerInterface
{
/**
* Finds an entry of the container by its identifier and returns it.
*
* @param string $id Identifier of the entry to look for.
*
* @throws NotFoundExceptionInterface No entry was found for **this** identifier.
* @throws ContainerExceptionInterface Error while retrieving the entry.
*
* @return mixed Entry.
*/
public function get(string $id);
/**
* Returns true if the container can return an entry for the given identifier.
* Returns false otherwise.
*
* `has($id)` returning true does not mean that `get($id)` will not throw an exception.
* It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`.
*
* @param string $id Identifier of the entry to look for.
*
* @return bool
*/
public function has(string $id);
}Tip
Kindly read the PSR-11 specification to understand more of its concept and how to conform on its interfaces.
The ContainerInterface is regarded as the foundation of the Slytherin package since a container must be identified with its dependencies to resolve Slytherin's required core components (e.g., HTTP, Middleware, Routing, etc.).
Slytherin also provides third-party implementations to the ContainerInterface for those who needs to use it right way and implement an improve version for the interface later. The said implementations can be found in the Container page.
ContainerInterfaceWith the usage of ContainerInterface, it is possible to create alternative implementations to improve the maintainability and reusability of application code. One of its alternative implementations is the Components implementations which has its own implementation page.
Prior to the concept of PSR-11, the Components class is used for handling Slytherin's core components. In the current version of Slytherin, the said class is also implemented to the ContainerInterface:
// app/web/index.php
-use Psr\Http\Message\ResponseInterface;
-use Psr\Http\Message\ServerRequestInterface;
use Rougin\Slytherin\Application;
-use Rougin\Slytherin\Container\Container;
+use Rougin\Slytherin\Components;
use Rougin\Slytherin\Http\Response;
use Rougin\Slytherin\Http\ServerRequest;
use Rougin\Slytherin\Routing\Dispatcher;
-use Rougin\Slytherin\Routing\DispatcherInterface;
use Rougin\Slytherin\Routing\Router;
// ...
+$components = new Components;
+
// Define HTTP objects that is compliant to PSR-07 standards ---
$request = new ServerRequest((array) $_SERVER);
+$components->setHttpRequest($request);
$response = new Response(http_response_code());
+$components->setHttpResponse($response);
// -------------------------------------------------------------
// ...
// ...then define it to a dispatcher ---
$dispatcher = new Dispatcher($router);
+
+$components->setDispatcher($dispatcher);
// -------------------------------------
-// Add the above objects through a container ------------
-$container = new Container;
-
-$container->set(ServerRequestInterface::class, $request);
-
-$container->set(ResponseInterface::class, $response);
-
-$container->set(DispatcherInterface::class, $dispatcher);
-// ------------------------------------------------------
// Lastly, run the application ------
-(new Application($container))->run();
+(new Application($components))->run();
// ----------------------------------Collection classThe Components class is only another name for the Collection class. More information for this class can be found in its own Collection page.
IntegrationInterfaceAs the previously discussed implementation is created prior to the ContainerInterface, there is another way for abstracting application code which is the IntegrationInterface implementation. A guide for the said implementation can be found in its own "IntegrationInterface" Implementation page.
The IntegrationInterface implementation is another way of organizing the application code and make it reusable. Its concept is similar to the Components class but their difference is that the IntegrationInterface is not being used exclusively for Slytherin's core components.
With this, it is possible for defining the instances needed to an application first prior receiving the HTTP request. The usage of the IntegrationInterface is also used to support specified third-party packages for Slytherin.
*Integration classesBelow is an example integration of the Routing component which is the RoutingIntegration. This integration will create a class that is based on the DispatcherInterface of the said component and will accept a RouterInterface from the configuration if provided:
namespace Rougin\Slytherin\Routing;
use Rougin\Slytherin\Container\ContainerInterface;
use Rougin\Slytherin\Integration\Configuration;
use Rougin\Slytherin\Integration\IntegrationInterface;
use Rougin\Slytherin\System;
class RoutingIntegration implements IntegrationInterface
{
// ...
/**
* Defines the specified integration.
*
* @param \Rougin\Slytherin\Container\ContainerInterface $container
* @param \Rougin\Slytherin\Integration\Configuration $config
* @return \Rougin\Slytherin\Container\ContainerInterface
*/
public function define(ContainerInterface $container, Configuration $config)
{
$dispatcher = new Dispatcher;
/** @var \Rougin\Slytherin\Routing\RouterInterface */
$router = $config->get('app.router', new Router);
if ($this->wants('fastroute'))
{
$dispatcher = new FastRouteDispatcher;
}
if ($this->wants('phroute'))
{
$dispatcher = new PhrouteDispatcher;
}
$container->set(System::DISPATCHER, $dispatcher);
return $container->set(System::ROUTER, $router);
}
// ...
}Note
All components in Slytherin have their own integration implementation (except for the Container component).
To add the RoutingIntegration to an application, use the integrate method from the Application class prior to the run method:
use Rougin\Slytherin\Application;
use Rougin\Slytherin\Components;
+use Rougin\Slytherin\Configuration;
use Rougin\Slytherin\Http\Response;
use Rougin\Slytherin\Http\ServerRequest;
-use Rougin\Slytherin\Routing\Dispatcher;
use Rougin\Slytherin\Routing\Router;
+use Rougin\Slytherin\Routing\RoutingIntegration;
require 'vendor/autoload.php';
$components = new Components;
+$config = new Configuration;
+
// ...
// Create a new router to add an HTTP route... ---
// ...
+
+$config->set('app.router', $router);
// -----------------------------------------------
// ...then define it to a dispatcher ---
-$dispatcher = new Dispatcher($router);
-
-$components->setDispatcher($dispatcher);
+$items = array(new RoutingIntegration);
// -------------------------------------
// Lastly, run the application ------
-(new Application($components))->run();
+$app = new Application($components);
+
+$app->integrate($items, $config)->run();
// ----------------------------------Based on the sample code above, the IntegrationInterface implementation can coexist with the previously discussed implementations (e.g., ContainerInterface, Components). It only depends on the proposed folder structure or implementation of an application.
Application as a RouterWith the IntegrationInterface, the Application class can also act as a Router (that is implemented in Rougin\Slytherin\Routing\RouterInterface) and uses the HttpIntegration and RoutingIntegration which can create a Slytherin application directly without specifying the said integrations manually. A separate guide for this special implementation can be found in the Using Application as a Router page.
This kind of implementation is a use-case for creating quick Slytherin application without specifying anything. Since it is based on the Application class, the previously discussed implementations like the ContainerInterface and IntegrationInterface can still be applied in this implementation.
The example code below can also be found from the The First "Hello World" page:
// app/web/index.php
use Rougin\Slytherin\Application;
// Load the Composer autoloader ----
$root = dirname(dirname(__DIR__));
require "$root/vendor/autoload.php";
// ---------------------------------
// Create a new application instance ---
$app = new Application;
// -------------------------------------
// Create a new HTTP route ---
$app->get('/', function ()
{
return 'Hello world!';
});
// ---------------------------
// Run the application ---
echo $app->run();
// -----------------------Then check the Running the web server section on how to run the example code.
Note
As the Application class is utilizing the Router class from Routing component, the defined methods of the latter can be used in the former.
As using IntegrationInterface is an alternative for setting Slytherin's components, its important part is defining third-party custom implementations using Slytherin's defined interfaces. A guide for the list of Slytherin's interfaces can be found in the Using Interfaces page.
In Slytherin, object interfaces are used to integrate its defined components. Below are the available components of Slytherin with their respective interface names:
Rougin\Slytherin\Container\ContainerInterface:namespace Rougin\Slytherin\Container;
use Psr\Container\ContainerInterface as PsrContainerInterface;
interface ContainerInterface extends PsrContainerInterface
{
/**
* Sets a new instance to the container.
*
* @param string $id
* @param mixed $concrete
* @return self
*/
public function set($id, $concrete);
}Note
The said ContainerInterface is an extended implementation of the PSR-11 standard (psr/container).
Container component can be found in its Container page.Routing component, it must be implemented in Rougin\Slytherin\Routing\DispatcherInterface:namespace Rougin\Slytherin\Routing;
interface DispatcherInterface
{
/**
* Dispatches against the provided HTTP method verb and URI.
*
* @param string $method
* @param string $uri
* @return \Rougin\Slytherin\Routing\RouteInterface
*
* @throws \BadMethodCallException
*/
public function dispatch($method, $uri);
/**
* Sets the router and parse its available routes if needed.
*
* @param \Rougin\Slytherin\Routing\RouterInterface $router
* @return self
*
* @throws \UnexpectedValueException
*/
public function setRouter(RouterInterface $router);
}Http component to Slytherin, must be implemented in a PSR-07 standard (psr/http-message):namespace Psr\Http\Message;
interface ServerRequestInterface extends RequestInterface
{
}namespace Psr\Http\Message;
interface ResponseInterface extends MessageInterface
{
}Rougin\Slytherin\Debug\ErrorHandlerInterface:namespace Rougin\Slytherin\Debug;
interface ErrorHandlerInterface
{
/**
* Registers the instance as an error handler.
*
* @return void
*/
public function display();
}psr/http-message and psr/http-server-middleware or http-interop/http-middleware) or through Rougin\Slytherin\Middleware\MiddlewareInterface:namespace Rougin\Slytherin\Middleware;
use Psr\Http\Message\ServerRequestInterface;
interface MiddlewareInterface
{
/**
* Process an incoming server request and return a response, optionally delegating
* to the next middleware component to create the response.
*
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param \Rougin\Slytherin\Middleware\HandlerInterface $handler
* @return \Psr\Http\Message\ResponseInterface
*/
public function process(ServerRequestInterface $request, HandlerInterface $handler);
}Rougin\Slytherin\Middleware\DispatcherInterface:namespace Rougin\Slytherin\Middleware;
interface DispatcherInterface extends MiddlewareInterface
{
/**
* Returns the list of added middlewares.
*
* @return \Rougin\Slytherin\Middleware\MiddlewareInterface[]
*/
public function getStack();
/**
* Add a new middleware to the end of the stack.
*
* @param mixed $middleware
* @return self
*/
public function push($middleware);
/**
* Sets a new stack of middlewares.
*
* @param mixed[] $stack
* @return self
*/
public function setStack($stack);
}Rougin\Slytherin\Template\RendererInterface:namespace Rougin\Slytherin\Template;
interface RendererInterface
{
/**
* Renders a template.
*
* @param string $template
* @param array<string, mixed> $data
* @return string
*/
public function render($template, array $data = array());
}Once the custom components were implemented in one of the defined interfaces from Slytherin, the said components must be defined using ContainerInterface. A guide for integrating the said components to Slytherin can be found in the Integrating Components page.
As discussed from the Using Interfaces page, custom components require its respective defined interface to be able to integrate a custom component to the Slytherin core. Integrating components require a PSR-11-compliant container.
Twig is one of the third-party template engines that has a built-in support in Slytherin. To integrate the said third-party package to Slytherin, the interface Rougin\Slytherin\Template\RendererInterface must be implemented to the said package:
namespace Rougin\Slytherin\Template;
class TwigRenderer implements RendererInterface
{
/**
* @var \Twig\Environment
*/
protected $twig;
// ...
/**
* Renders a template.
*
* @param string $template
* @param array<string, mixed> $data
* @param string $extension
* @return string
*/
public function render($template, array $data = array(), $extension = 'html')
{
return $this->twig->render("$template.$extension", $data);
}
}// app/web/index.php
use Rougin\Slytherin\Container\Container;
use Rougin\Slytherin\Template\RendererInterface;
use Rougin\Slytherin\Template\TwigRenderer;
// ...
$path = __DIR__ . '/templates/directory';
$loader = new Twig_Loader_Filesystem($path);
$twig = new Twig_Environment($loader);
$renderer = new TwigRenderer($twig);
$container = new Container;
$container->set(RendererInterface::class, $renderer);In the example code above, Twig_Environment is used as a dependency but according to Twig's documentation. The said class must be defined prior to its integration to the RendererInterface.
Note
A detailed description about the concept of dependency injection containers can be found in the Container page.
Components classIf the application structure uses the Components class, the custom component can be defined in its available mutators:
// app/web/index.php
-use Rougin\Slytherin\Container\Container;
-use Rougin\Slytherin\Template\RendererInterface;
+use Rougin\Slytherin\Components;
use Rougin\Slytherin\Template\TwigRenderer;
// ...
$renderer = new TwigRenderer($twig);
-$container = new Container;
+$components = new Components;
// Sets the instance to the RendererInterface -------
-$container->set(RendererInterface::class, $renderer);
+$components->setTemplate($renderer);
// --------------------------------------------------Tip
More information can be found in the Collection page regarding its available accessors and mutators.
If a specified third-party package is supported by Slytherin, a component integration can be used to improve the maintainability of the application code:
// app/web/index.php
+use Rougin\Slytherin\Application;
use Rougin\Slytherin\Components;
-use Rougin\Slytherin\Template\TwigRenderer;
+use Rougin\Slytherin\Configuration;
+use Rougin\Slytherin\Template\RendererIntegration;
// ...
+$config = new Configuration;
+
+$app = new Application;
+
// Defines the Twig_Environment instance ---
$path = __DIR__ . '/templates/directory';
-$loader = new Twig_Loader_Filesystem($path);
-
-$twig = new Twig_Environment($loader);
+$config->set('app.views', $path);
-$renderer = new TwigRenderer($twig);
+$twig = new RendererIntegration;
// -----------------------------------------
$components = new Components;
// Sets the instance to the RendererInterface -------
-$components->setTemplate($renderer);
+$app->integrate($twig, $config)->run();
// --------------------------------------------------Each implementation strategies discussed above can also be seen in each of the component documentation in the Components section.
The Collection class is a utility class that handles the setting up of Slytherin's components. The said class was previously named as Components and was mainly used prior to the official implementation of the PSR-11 standard.
Since the Components class implements the ContainerInterface, the said class can be used as an argument to the Application class:
// app/web/index.php
use Rougin\Slytherin\Application;
use Rougin\Slytherin\Components;
use Rougin\Slytherin\Http\Response;
use Rougin\Slytherin\Http\ServerRequest;
use Rougin\Slytherin\Routing\Dispatcher;
use Rougin\Slytherin\Routing\Router;
require 'vendor/autoload.php';
$components = new Components;
// Define HTTP objects that is compliant to PSR-07 standards ---
$request = new ServerRequest((array) $_SERVER);
$components->setHttpRequest($request);
$response = new Response(http_response_code());
$components->setHttpResponse($response);
// -------------------------------------------------------------
// Create a new router to add an HTTP route... ---
$router = new Router;
$router->get('/', function ()
{
return 'Hello world!';
});
// -----------------------------------------------
// ...then define it to a dispatcher ---
$dispatcher = new Dispatcher($router);
$components->setDispatcher($dispatcher);
// -------------------------------------
// Lastly, run the application ------
(new Application($components))->run();
// ----------------------------------namespace Rougin\Slytherin\Component;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Rougin\Slytherin\Container\ContainerInterface;
use Rougin\Slytherin\Debug\ErrorHandlerInterface;
use Rougin\Slytherin\Middleware\DispatcherInterface as Middleware;
use Rougin\Slytherin\Routing\DispatcherInterface as Routing;
use Rougin\Slytherin\Template\RendererInterface;
class Collection implements ContainerInterface
{
/**
* Returns the dispatcher.
*
* @return \Rougin\Slytherin\Dispatching\DispatcherInterface
*/
public function getDispatcher();
/**
* Returns the error handler.
*
* @return \Rougin\Slytherin\Debug\ErrorHandlerInterface
*/
public function getErrorHandler();
/**
* Returns the HTTP request and response.
*
* @return mixed
*/
public function getHttp();
/**
* Returns the HTTP request.
*
* @return \Psr\Http\Message\ServerRequestInterface
*/
public function getHttpRequest();
/**
* Returns the HTTP response.
*
* @return \Psr\Http\Message\ResponseInterface
*/
public function getHttpResponse();
/**
* Returns the middleware.
*
* @return \Rougin\Slytherin\Middleware\DispatcherInterface
*/
public function getMiddleware();
/**
* Returns the template.
*
* @return \Rougin\Slytherin\Template\RendererInterface
*/
public function getTemplate();
/**
* Sets the dispatcher.
*
* @param \Rougin\Slytherin\Dispatching\DispatcherInterface $dispatcher
* @return self
*/
public function setDispatcher(Routing $dispatcher);
/**
* Sets the error handler.
*
* @param \Rougin\Slytherin\Debug\ErrorHandlerInterface $debugger
* @return self
*/
public function setErrorHandler(ErrorHandlerInterface $debugger);
/**
* Sets the HTTP components.
*
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param \Psr\Http\Message\ResponseInterface $response
* @return self
*/
public function setHttp(ServerRequestInterface $request, ResponseInterface $response);
/**
* Sets the HTTP request.
*
* @param \Psr\Http\Message\ServerRequestInterface $request
* @return self
*/
public function setHttpRequest(ServerRequestInterface $request);
/**
* Sets the HTTP response.
*
* @param \Psr\Http\Message\ResponseInterface $response
* @return self
*/
public function setHttpResponse(ResponseInterface $response);
/**
* Sets the middleware.
*
* @param \Rougin\Slytherin\Middleware\DispatcherInterface $middleware
* @return self
*/
public function setMiddleware(Middleware $middleware);
/**
* Sets the template.
*
* @param \Rougin\Slytherin\Template\RendererInterface $template
* @return self
*/
public function setTemplate(RendererInterface $template);
}The Configuration class serves as a storage for configuration data. It supports dot-notation key access for nested values and can also load from PHP files in a directory. It is primarily used in conjunction with IntegrationInterface implementations to pass configuration values for bootstrapping.
The Configuration class accepts data in three ways:
load() internallyset()Values are retrieved using get() which supports dot notation for accessing nested keys. When a key does not exist, get() returns the provided default value, or null if no default is specified.
The set() method stores a value at a given key, supporting the same dot notation. If the third argument $fromFile is set to true, the value is treated as a file path and loaded via require.
The load() method scans a directory for *.php files and merges them into the configuration, where each file's basename (without .php) becomes a top-level key.
// app/web/index.php
use Rougin\Slytherin\Integration\Configuration;
$data = array('app' => array());
$data['app']['name'] = 'My Application';
$data['app']['debug'] = true;
$database = array('name' => 'test');
$database['username'] = 'root';
$database['password'] = 'secret';
$data['database'] = $database;
$config = new Configuration($data);
// Access top-level keys -----------
$appName = $config->get('app.name'); // 'My Application'
// ---------------------------------
// Access nested keys with dot notation ----
$dbUser = $config->get('database.username'); // 'root'
// -----------------------------------------
// Provide a default value for missing keys ---
$dbPort = $config->get('database.port', 3306); // 3306
// --------------------------------------------// app/web/index.php
use Rougin\Slytherin\Integration\Configuration;
// Assuming a directory "config/" with:
// config/app.php → array('name' => 'My App')
// config/database.php → array('host' => 'localhost', 'name' => 'test')
$config = new Configuration;
$config->load(__DIR__ . '/config');
$appName = $config->get('app.name'); // 'My App'
$dbHost = $config->get('database.host'); // 'localhost'// app/web/index.php
use Rougin\Slytherin\Application;
use Rougin\Slytherin\Integration\Configuration;
use Rougin\Slytherin\Routing\Router;
use Rougin\Slytherin\Routing\RoutingIntegration;
$config = new Configuration;
// Set a custom router instance ----
$router = new Router;
$router->get('/', function ()
{
return 'Hello world!';
});
$config->set('app.router', $router);
// ---------------------------------
$app = new Application;
// Pass the configuration when integrating ------
$app->integrate(new RoutingIntegration, $config);
// ----------------------------------------------
$app->run();namespace Rougin\Slytherin\Integration;
class Configuration
{
/**
* @param array<string, mixed>|string|null $data
*/
public function __construct($data = null);
/**
* Returns the value from the specified key.
*
* @param string $key
* @param mixed|null $default
* @return mixed
*/
public function get($key, $default = null);
/**
* Loads the configuration from a specified directory.
*
* @param string $directory
* @return array<string, mixed>
*/
public function load($directory);
/**
* Sets the value to the specified key.
*
* @param string $key
* @param mixed $value
* @param boolean $fromFile
* @return self
*/
public function set($key, $value, $fromFile = false);
}A container is a data structure whose instances are collections of other objects. Containers store objects in an organized way that follows specific access rules.
For this concept on containers, kindly check the code below on how to implement a class with a constructor:
class Foo
{
public function baz()
{
// ...
}
}
class Bar
{
protected $foo;
public function __construct(Foo $foo)
{
$this->foo = $foo;
}
public function booz()
{
return $this->foo->baz();
}
}$bar = new Bar(new Foo);With the example code above, the Bar class must always have an instance of Foo.
To use the Bar class, it needs to be implemented again like in the example code above which can be cumbersome. One of the solutions for this is to store it into a container once so it can be accessed anywhere in the application code:
use Rougin\Slytherin\Container\Container;
$container = new Container;
$bar = new Bar(new Foo);
$container->set('Bar', $bar);
// Returns an instance of Bar ---
$new = $container->get('Bar');
// ------------------------------Some third-party packages for container provide additional functionalities like auto-wiring dependencies and more. With this, Slytherin supported the following third-party packages that can be integrated seamlessly to Slytherin core once their respective packages were installed:
AurynContainer - uses the Auryn package by Daniel Lowrey (rdlowrey/auryn)LeagueContainer - uses the Container package from The PHP League (league/container)If not using the supported packages above, a list of packages that implements the dependency injection design pattern are also listed at the awesome-php repository. Once selected a specified package, it must be implemented in ContainerInterface:
namespace Rougin\Slytherin\Container;
use Psr\Container\ContainerInterface as PsrContainerInterface;
interface ContainerInterface extends PsrContainerInterface
{
/**
* Sets a new instance to the container.
*
* @param string $id
* @param mixed $concrete
* @return self
*/
public function set($id, $concrete);
}Error handlers are used for showing a beautiful page on captured errors during the runtime of an application. An error handler may show a stack trace how the process arrived in the said error and may show possible fixes and suggestions on how to handle it.
Caution
Errors are a good way to display what is wrong in an application. However, it can be very dangerous to show a very detailed error message into production as it might get used as an entry point by hackers on how to gain access to the application. With this, showing errors must be disabled in a production environment.
Whoops is one of the third-party error handlers that is supported by Slytherin. To integrate it to the Slytherin core, kindly use the WhoopsErrorHandler provided by Slytherin then set the newly created instance into a Container:
use Rougin\Slytherin\Container\Container;
use Rougin\Slytherin\Debug\ErrorHandlerInterface;
use Rougin\Slytherin\Debug\WhoopsErrorHandler;
$container = new Container;
$class = ErrorHandlerInterface::class;
$whoops = new WhoopsErrorHandler(new Whoops\Run);
$container->set($class, $whoops);ErrorHandlerIntegrationThe ErrorHandlerIntegration is a built-in integration by Slytherin for its ErrorHandler component to remove the need of manually creating an instance for the said component:
use Rougin\Slytherin\Application;
use Rougin\Slytherin\Configuration;
use Rougin\Slytherin\Debug\ErrorHandlerIntegration;
$config = new Configuration;
$config->set('app.environment', 'development');
$app = new Application(null, $config);
$app->integrate(new ErrorHandlerIntegration);
echo $app->run();Note
The respective instance that will be created for the said integration is only limited for the third-party packages that are currently supported by Slytherin. The built-in implementation will be used if no third-party packages are installed for the said component.
Slytherin has built-in support for error handlers if using the ErrorHandlerIntegration. Besides the default PHP implementation of handling errors (the ErrorHandler class), the following third-party packages below can be integrated seamlessly to the Slytherin core once their respective packages were installed:
WhoopsErrorHandler - uses the Whoops package by Filipe Dobreira (filp/whoops)If not using the supported packages above, a list of packages that implements debugging functionalities are also listed at the awesome-php repository. Once selected a specified package, it must be implemented in ErrorHandlerInterface:
namespace Rougin\Slytherin\Debug;
interface ErrorHandlerInterface
{
/**
* Registers the instance as an error handler.
*
* @return void
*/
public function display();
}Although this is an optional component to Slytherin, the Middleware component is being used through the usage of its interfaces. The main goal of a middleware is to manipulate both the incoming HTTP request and the outgoing HTTP response.
There is a Medium post that describes the usage of middlewares and its importance in PHP frameworks.
To create a middleware, the middleware must be implemented in MiddlewareInterface:
namespace Rougin\Sample;
use Psr\Http\Message\ServerRequestInterface;
use Rougin\Slytherin\Middleware\HandlerInterface;
use Rougin\Slytherin\Middleware\MiddlewareInterface;
class Hello implements MiddlewareInterface
{
public function process(ServerRequestInterface $request, HandlerInterface $handler)
{
$response = $handler->handle($request);
$response->getBody()->write('Hello world!');
return $response;
}
}Note
The example middleware above is currently implemented in Slytherin's own MiddlewareInterface. However, other middlewares that implements on the variants of the PSR-15 standard (http-interop/http-middleware or psr/http-server-middleware) are also supported by converting them back to the built-in MiddlewareInterface of Slytherin.
To use the recently defined middleware, a middleware dispatcher must be used that is implemented to the DispatcherInterface:
// app/web/index.php
use Rougin\Sample\Hello;
use Rougin\Slytherin\Middleware\Dispatcher;
use Rougin\Slytherin\System\Lastone;
// A ServerRequestInterface must be used as its parameter ---
/** @var \Psr\Http\Message\ServerRequestInterface $request */
// ----------------------------------------------------------
$dispatch = new Dispatcher;
// Push the middleware on the end of the "stack"
$dispatch->push(new Hello);
// A final handler must return an empty ResponseInterface ---
$final = new Lastone;
// ----------------------------------------------------------
// Dispatches the defined "stack" using the provided request ---
echo $dispatch->process($request, $final);
// -------------------------------------------------------------The example code above should return Hello world! after executing the said code in a terminal:
$ php app/web/index.php
"Hello world!"MiddlewareIntegrationThe MiddlewareIntegration is a built-in integration for the Middleware component that can be used for defining only the available middlewares to be added to the Application instance:
use Rougin\Sample\Hello;
-use Rougin\Slytherin\Middleware\Dispatcher;
-use Rougin\Slytherin\System\Lastone;
+use Rougin\Slytherin\Application;
+use Rougin\Slytherin\Configuration;
+use Rougin\Slytherin\Middleware\MiddlewareIntegration;
// A ServerRequestInterface must be used as its parameter ---
/** @var \Psr\Http\Message\ServerRequestInterface $request */
// ----------------------------------------------------------
-$dispatch = new Dispatcher;
+$config = new Configuration;
// Push the middleware on the end of the "stack" ---
-$dispatch->push(new Hello);
+$config->set('app.middlewares', array(new Hello));
// -------------------------------------------------
-// A final handler must return an empty ResponseInterface ---
-$final = new Lastone;
-// ----------------------------------------------------------
-
// Dispatches the defined "stack" using the provided request ---
-echo $dispatch->process($request, $final);
+$app = new Application(null, $config);
+
+$app->integrate(new MiddlewareIntegration);
+
+echo $app->run();
// -------------------------------------------------------------Note
The respective instance that will be created for the said integration is only limited for the third-party packages that are currently supported by Slytherin. The built-in implementation will be used if no third-party packages are installed for the said component.
If using the MiddlewareIntegration, Slytherin can integrate the following third-party packages below if their respective packages are installed:
StratigilityDispatcher - uses the Zend Stratigility package by the Zend Framework (zendframework/zend-stratigility)If not using the supported packages above, a list of packages that handles HTTP routing are also listed at the awesome-php repository. Once selected a specified package, it must be implemented in DispatcherInterface:
namespace Rougin\Slytherin\Middleware;
interface DispatcherInterface extends MiddlewareInterface
{
/**
* Returns the list of added middlewares.
*
* @return \Rougin\Slytherin\Middleware\MiddlewareInterface[]
*/
public function getStack();
/**
* Add a new middleware to the end of the stack.
*
* @param mixed $middleware
* @return self
*/
public function push($middleware);
/**
* Sets a new stack of middlewares.
*
* @param mixed[] $stack
* @return self
*/
public function setStack($stack);
}Routing is one of the required components of Slytherin that stores HTTP routes (through RouterInterface) and matches the result from the given HTTP method and URI through a dispatcher (DispatcherInterface). The difference between a router and a dispatcher is explained carefully in this post.
Below code shows an example how the storing HTTP routes and dispatching them works:
namespace Rougin\Sample;
class Hello
{
public function greet()
{
return 'Hello world!';
}
}// app/web/index.php
use Rougin\Slytherin\Routing\Dispatcher;
use Rougin\Slytherin\Routing\Router;
$router = new Router;
$router->get('/', 'Rougin\Sample\Hello@greet');
$dispatch = new Dispatcher($router);
$method = $_SERVER['REQUEST_METHOD'];
$uri = $_SERVER['REQUEST_URI'];
/** @var \Rougin\Slytherin\Routing\RouteInterface */
$route = $dispatch->dispatch($method, $uri);The dispatch method should return a RouteInterface that contains the route details. If there is no route found against the specified $method and $uri, a BadMethodCallException should be thrown.
There may be scenarios that a specified middleware can only be applied to a specified HTTP route. In such cases, the middleware must be added next to specifying its handler:
+use Psr\Http\Message\ResponseInterface;
use Rougin\Slytherin\Application;
+use Rougin\Slytherin\Sample\Handlers\Interop;
// ...
// Create a new HTTP route ---
-$app->get('/', function ()
+$fn = function (ResponseInterface $response)
{
- return 'Hello world!';
-});
+ return $response;
+};
+
+$app->get('/', $fn, new Interop);
// ---------------------------
// ...When using middlewares, the output of a route handler should return in Psr\Http\Message\ResponseInterface to apply the middleware's logic.
Tip
When using callbacks as route handlers, callbacks that return other than the ResponseInterface will be automatically converted to the same interface.
Note
The code example above was based on the Using Application as a Router page.
RoutingIntegrationThe RoutingIntegration can be used to define only the HTTP routes without manually specifying its dispatcher:
// app/web/index.php
// ...
use Rougin\Slytherin\Application;
use Rougin\Slytherin\Configuration;
use Rougin\Slytherin\Routing\Router;
use Rougin\Slytherin\Routing\RoutingIntegration;
// ...
$config = new Configuration;
// ...
// Create a new router to add an HTTP route... ---
$router = new Router;
$router->get('/', function ()
{
return 'Hello world!';
});
$config->set('app.router', $router);
// -----------------------------------------------
// ...then define it to a dispatcher ---
$items = array(new RoutingIntegration);
// -------------------------------------
$app = new Application(null, $config);
echo $app->integrate($items)->run();Note
The respective instance that will be created for the said integration is only limited for the third-party packages that are currently supported by Slytherin. The built-in implementation will be used if no third-party packages are installed for the said component.
If using the RoutingIntegration, Slytherin can integrate the following third-party packages below if their respective packages are installed:
FastRouteDispatcher, FastRouteRouter - uses the FastRoute package by Nikita Popov (nikic/fast-route)PhrouteDispatcher, PhrouteRouter - uses the Phroute package by Joe Green (phroute/phroute)If not using the supported packages above, a list of packages that handles HTTP routing are also listed at the awesome-php repository. Once selected a specified package, it must be implemented in DispatcherInterface and RouterInterface:
namespace Rougin\Slytherin\Routing;
interface DispatcherInterface
{
/**
* Dispatches against the provided HTTP method verb and URI.
*
* @param string $method
* @param string $uri
* @return \Rougin\Slytherin\Routing\RouteInterface
*
* @throws \BadMethodCallException
*/
public function dispatch($method, $uri);
/**
* Sets the router and parse its available routes if needed.
*
* @param \Rougin\Slytherin\Routing\RouterInterface $router
* @return self
*
* @throws \UnexpectedValueException
*/
public function setRouter(RouterInterface $router);
}namespace Rougin\Slytherin\Routing;
interface RouterInterface
{
/**
* Adds a new raw route.
*
* @param string $method
* @param string $uri
* @param callable|string[]|string $handler
* @param \Rougin\Slytherin\Middleware\MiddlewareInterface[]|string[] $middlewares
* @return self
*/
public function add($method, $uri, $handler, $middlewares = array());
/**
* Merges a listing of parsed routes to current one.
*
* @param \Rougin\Slytherin\Routing\RouteInterface[] $routes
* @return self
*/
public function merge(array $routes);
/**
* Returns a listing of parsed routes.
*
* @param \Rougin\Slytherin\Routing\RouteInterface[] $routes
* @return mixed|null
*/
public function parsed(array $routes = array());
/**
* Returns a listing of available routes.
*
* @return \Rougin\Slytherin\Routing\RouteInterface[]
*/
public function routes();
}The Http component is one of the core components of Slytherin to handle HTTP variables in a nice abstraction. Although the PHP has provided built-in functions specifically for handling HTTP, it is recommended to use its object-oriented abstractions through the PSR-07 standard to provide common interfaces for representing HTTP messages.
Slytherin has already a built-in implementation of the Http component. The Slytherin core requires instances that implements in ServerRequestInterface and ResponseInterface respectively from the said PSR-07 standard:
use Rougin\Slytherin\Http\ServerRequest;
use Rougin\Slytherin\Http\Response;
/** @var \Psr\Http\Message\ServerRequestInterface */
$request = new ServerRequest($_SERVER, $_COOKIE, $_GET, $_FILES, $_POST);
/** @var \Psr\Http\Message\ResponseInterface */
$response = new Response(http_response_code());But if there is a preferred third-party to be used, the said package should be implemented in PSR-07 standard with the required interfaces:
use Zend\Diactoros\Response;
use Zend\Diactoros\ServerRequestFactory;
// Zend Diactoros is a PSR-07 compliant package ---
/** @var \Psr\Http\Message\ServerRequestInterface */
$request = ServerRequestFactory::fromGlobals();
/** @var \Psr\Http\Message\ResponseInterface */
$response = new Response;
// ------------------------------------------------Once the required instances were instantiated, these must be included into a Container to be added in the Application instance:
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Rougin\Slytherin\Application;
use Rougin\Slytherin\Container\Container;
$container = new Container;
// ...
// Include the required instances to the container ------
$container->set(ServerRequestInterface::class, $request);
$container->set(ResponseInterface::class, $response);
// ------------------------------------------------------
(new Application($container))->run();HttpIntegrationIf using the Application class, the HttpIntegration can be used for creating the required HTTP request and response instances without interacting to its respective classes:
use Rougin\Slytherin\Application;
use Rougin\Slytherin\Configuration;
use Rougin\Slytherin\Http\HttpIntegration;
$config = new Configuration;
// Set the HTTP variables to the config ---
$config->set('app.http.cookies', $_COOKIE);
$config->set('app.http.files', $_FILES);
$config->set('app.http.get', $_GET);
$config->set('app.http.post', $_POST);
$config->set('app.http.server', $_SERVER);
// ----------------------------------------
// Add the integration the run the app ---
$items = array(new HttpIntegration);
$app = new Application(null, $config);
echo $app->integrate($items)->run();
// ---------------------------------------Note
The respective instance that will be created for the said integration is only limited for the third-party packages that are currently supported by Slytherin. The built-in implementation will be used if no third-party packages are installed for the said component.
Although the Http component has its own implementation of the PSR-07 standard through HttpIntegration, Slytherin supports the following third-party packages below that can be integrated seamlessly to Slytherin core once their respective packages were installed:
zendframework/zend-diactoros)Note
If one of the supported third-party packages was installed, their current implementations will be included by default to the container to be used by the Application instance.
If not using the supported packages above, a list of packages that implements the PSR-07 standard are also listed at the awesome-php repository. Once selected a specified package, it must be defined in ContainerInterface with the interfaces Psr\Http\Message\ServerRequestInterface and Psr\Http\Message\ResponseInterface to use it to the Slytherin core.
The Template is an optional component for Slytherin in handling HTML-like views. Although PHP as the programming language is a template engine, having an abstract for the said template engine can add additional features such as separating concerns between the data and its corresponding view template.
Slytherin provided an implementation for its own Template component. The said component is implemented by the Rougin\Slytherin\Template\RendererInterface:
// /views/hello.php
"Hello world!"// app/web/index.php
use Rougin\Slytherin\Template\Renderer;
// Set the location of the template views ---
$path = __DIR__ . '/views';
// ------------------------------------------
$renderer = new Renderer($path);
echo $renderer->render('hello');The example code above should return Hello world! after executing the said code in a terminal:
$ php app/web/index.php
"Hello world!"RendererIntegrationWhen using the Application class, the RendererIntegration can be used to create the RendererInterface without defining it manually:
use Rougin\Slytherin\Application;
use Rougin\Slytherin\Configuration;
use Rougin\Slytherin\Template\RendererIntegration;
$config = new Configuration;
// Set the location of the template views ----
$config->set('app.views', __DIR__ . '/views');
// -------------------------------------------
// Add the integration the run the app ---
$items = array(new RendererIntegration);
$app = new Application(null, $config);
echo $app->integrate($items)->run();
// ---------------------------------------Note
The respective instance that will be created for the said integration is only limited for the third-party packages that are currently supported by Slytherin. The built-in implementation will be used if no third-party packages are installed for the said component.
Using the built-in implementation of the Template component through RendererIntegration only provides basic functionalities like passing data to the template view. For other functionalities related to templating, Slytherin supports the following third-party packages below that can be integrated seamlessly to Slytherin core once their respective packages were installed:
TwigRenderer - uses the Twig package by Symfony (twig/twig)If not using the supported third-party packages above, a list of third-party packages that implements the templating functionality are also listed at the awesome-php repository. Once selected a specified package, it must be implemented in RendererInterface:
namespace Rougin\Slytherin\Template;
interface RendererInterface
{
/**
* Renders a template.
*
* @param string $template
* @param array<string, mixed> $data
* @return string
*/
public function render($template, array $data = array());
}