Php

The behavior of the site can be customized using PHP.
To do this, we put PHP files in the config folder path_php and we respect some PHP programming principles.

PHP files embedded with some custom code must return an object, this object can be of two types.

The returned object implements the interface Arnapou\SimpleSite\PhpCode

interface Arnapou\SimpleSite\PhpCode
{
    public function init(): void;
}

Like in the php/twig.php example which injects data from a yaml table into a global twig variable:

<?php

declare(strict_types=1);

use Arnapou\SimpleSite\SimpleSite;
use Arnapou\SimpleSite\PhpCode;

return new class() implements PhpCode {
    public function init(): void
    {
        $twig = SimpleSite::twig();
        $parameters = SimpleSite::db()->getTable('twig_globals');

        foreach ($parameters as $key => $data) {
            $twig->addGlobal($key, $data['value'] ?? '');
        }
        $twig->addGlobal('simplesite_phar_size', filesize(__DIR__.'/../../bin/simplesite.phar'));

        $twig->addFilter(
            new \Twig\TwigFilter(
                'preg_replace',
                static fn(string $source, string $pattern, string $replace) => preg_replace($pattern, $replace, $source)
            )
        );
    }
};

The returned object extends the class Arnapou\SimpleSite\Controller

Requirement to implement the abstract configure method (for routes):

abstract class Arnapou\SimpleSite\Controller
{
    abstract public function configure(): void;
}

Like in the php/controller.php example which declares a route hello to render a dummy page:

<?php

declare(strict_types=1);

use Arnapou\Psr\Psr7HttpMessage\Response;
use Arnapou\SimpleSite\Controller;

return new class() extends Controller {
    public function configure(): void
    {
        $this->addRoute('hello-{name}', $this->hello(...), 'hello')
            ->setRequirement('name', '[a-zA-Z]+');
    }

    public function hello(string $name): Response
    {
        $this->logger()->debug("Hello $name");
        $this->logger()->info("Hello $name");
        $this->logger()->notice("Hello $name");
        $this->logger()->warning("Hello $name");
        $this->logger()->error("Hello $name");
        $this->logger()->critical("Hello $name");
        $this->logger()->alert("Hello $name");
        $this->logger()->emergency("Hello $name");

        return $this->render('@templates/demo/hello.twig', ['name' => $name]);
    }
};

Example of page: link generated with {{ path('hello', { name: 'world'}) }}

Custom listener on routes

Custom listeners can be added to change the behavior of the site: change the response for example and/or bypass the routing.

Example php/event_hook.php which bypasses the routing to send another page:

<?php

declare(strict_types=1);

use Arnapou\Psr\Psr15HttpHandlers\Routing\Event\ServerRequestEvent;
use Arnapou\Psr\Psr15HttpHandlers\Routing\Listener\ServerRequestListenerInterface;
use Arnapou\Psr\Psr7HttpMessage\HtmlResponse;
use Arnapou\SimpleSite\SimpleSite;
use Arnapou\SimpleSite\PhpCode;
use Arnapou\SimpleSite\Controller;

return new class() implements PhpCode {
    public function init(): void
    {
        SimpleSite::router()->addListener($this->getHackListener(), Controller::PRIORITY_HIGHEST);
    }

    private function getHackListener(): ServerRequestListenerInterface
    {
        return new class() implements ServerRequestListenerInterface {
            public function __invoke(ServerRequestEvent $event): void
            {
                if ($event->request->getQueryParams()['killme'] ?? false) {
                    $event->response = new HtmlResponse('<h1>Arrrgghh .... I am killed ...</h1>', 500);
                    $event->stoppedPropagation = true;
                }
            }
        };
    }
};

Tester la page: