Concepts

Alias

Alias are a convenient way to retrieve paths in the application. They are prefixed by the character @. By default, 3 paths were created in the application initialization: @app is the application root path, @webroot is the public directory root path and @web is the uri base path.

Use Piko::getAlias() to retrieve a path or Piko::setAlias() to register a path.

Example using getAlias() and setAlias():

echo Piko::getAlias('@app/modules/site'); // /usr/local/share/myapp/modules/site
echo Piko::getAlias('@webroot/documents'); // /var/www/documents
echo Piko::getAlias('@web/css/styles.css'); // /css/styles.css

Piko::setAlias('@lib', '/usr/local/share/lib');
echo Piko::getAlias('@lib/pdf/model.pdf'); // /usr/local/share/lib/pdf/model.pdf

Component

Some objects need to be retrieved globally in the application. They are application singletons. In a Piko application, these objects are called “Components”

Common components are db connexion (PDO), logger, view rendering engine, user session manager, …

These components are declared in the components section of the application configuration using a key-value paired array where keys are components class name and values could be either:

Example of components configuration:

use Piko\View;
use Monolog\Logger;
use Monolog\StreamHandler;

//...
'components' => [
    View::class => [
        // set a public property
        'charset' => 'ISO-8859-1' // (Default charset is UTF-8)
    ],
    Logger::class => function() {
        $logger = new Logger('app');
        $logger->pushHandler(new StreamHandler( __DIR__ . '/../var/log/app.log', Logger::DEBUG));

        return $logger;
    },
    PDO::class => [
        'construct' => [
            'mysql:dbname=' . getenv('MYSQL_DB') . ';host=' . getenv('MYSQL_HOST'),
            getenv('MYSQL_USER'),
            getenv('MYSQL_PASSWORD'),
        ]
    ],
],
// ...

Once declared, their instances can be accessed throw the application using Application::getComponent() method. Therefore, the application can act like a container and it is possible to apply the Dependencies Injection pattern through this mechanism.

Note that these components are loaded lazily if they are declared as an array in the configuraton. They are instantiated the first time you access them by using Application::getComponent() method.

Events

Within Piko, its possible to dispatch and listen for events. This feature can be activated using the trait EventHandlerTrait:

class MyClass
{
    use Piko\EventHandlerTrait;
}

The trigger method is used to trigger an event and the on method is used to listen for that event.

Example of event triggering in MyClass:

class MyConnectEvent
{
    public $username = '';
    
    public function __construct($username)
    {
        $this->username = $username;
    }
}

class MyClass
{
    use Piko\EventHandlerTrait;
    
    private $username = '';
    
    public function connect()
    {
        $event = $this->trigger(new MyConnectEvent($this->username));

        echo $event->username;
    }
}

Example of listening event of MyClass:

$c = new MyClass();

$c->on(MyConnectEvent::class, function(MyConnectEvent $event) {
    $event->username = 'Paul';
});

$c->connect(); // Paul

At a lower level, the eventHandlerTrait uses the package piko/event-dispatcher, which is a PSR-14 implementation.

Behaviors

The BehaviorTrait may be used to inject dynamically a non-existing method into an object

To attach a behavior, use attachBehavior method.

Example:

class MyClass
{
    use Piko\BehaviorTrait;
}

$c = new MyClass(]);

$c->attachBehavior('disconnect', function() {
    echo 'I am disconnected!';
});

$con->disconnect(); // I am disconnected!

Middleware

In order to obtain a response, a Piko based application sends a request to a FIFO queue of Middlewares.

A middleware is a kind of sub-application that sits between the web server and the web application, intercepting requests and responses to perform some action.

Middlewares in Piko implements the standard PSR-15 Psr\Http\Server\MiddlewareInterface

Here is an illustration of how middleware works in a Piko application:

App  --> request  --> MiddlewareA  --> request   --> MiddlewareB
App <-- response <--  MiddlewareA <--  response <--  MiddlewareB

The request is an instance of Psr\Http\Message\ServerRequestInterface and the response an instance of Psr\Http\Message\ResponseInterface

Actually, the Piko ModularApplication is a queue of two middlewares : BootstrapMiddleware and RoutingMiddleware

Before the application start, it’s possible to pipe a custom middleware.

For instance, to inject the Access-Control-Allow-Origin header in the application response:

namespace app\lib;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

final class CorsMiddleware implements MiddlewareInterface
{
    /**
     * {@inheritDoc}
     * @see \Psr\Http\Server\MiddlewareInterface::process()
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $response = $handler->handle($request);

        return $response->withHeader('Access-Control-Allow-Origin', '*');
    }
}

In the entry script:

use Piko\ModularApplication;
use app\lib\CorsMiddleware;

require '../vendor/autoload.php';

$config = require '../config.php';
$app = new ModularApplication($config);
$app->pipe(new CorsMiddleware());
$app->run();