<?php
declare(strict_types=1);
namespace Viserio\Component\View;

use Viserio\Component\Contract\Filesystem\Filesystem as FilesystemContract;
use Viserio\Component\Contract\OptionsResolver\RequiresComponentConfig as RequiresComponentConfigContract;
use Viserio\Component\Contract\OptionsResolver\RequiresMandatoryOptions as RequiresMandatoryOptionsContract;
use Viserio\Component\Contract\View\Exception\InvalidArgumentException;
use Viserio\Component\Contract\View\Finder as FinderContract;
use Viserio\Component\OptionsResolver\Traits\OptionsResolverTrait;

class ViewFinder implements FinderContract, RequiresComponentConfigContract, RequiresMandatoryOptionsContract
{
    use OptionsResolverTrait;

    /**
     * The array of active view paths.
     *
     * @var array
     */
    protected $paths = [];

    /**
     * The array of views that have been located.
     *
     * @var array
     */
    protected $views = [];

    /**
     * The namespace to file path hints.
     *
     * @var array
     */
    protected $hints = [];

    /**
     * Register a view extension with the finder.
     *
     * @var array
     */
    protected static $extensions = [
        'php',
        'phtml',
        'css',
        'js',
        'md',
    ];

    /**
     * The filesystem instance.
     *
     * @var \Viserio\Component\Contract\Filesystem\Filesystem
     */
    private $files;

    /**
     * Create a new file view loader instance.
     *
     * @param \Viserio\Component\Contract\Filesystem\Filesystem $files
     * @param array|\ArrayAccess                                $config
     */
    public function __construct(FilesystemContract $files, $config)
    {
        $this->files = $files;
        $options     = self::resolveOptions($config);
        $this->paths = $options['paths'];

        if (isset($options['extensions']) && \is_array($options['extensions'])) {
            foreach ($options['extensions'] as $extension) {
                $this->addExtension($extension);
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function getPaths(): array
    {
        return $this->paths;
    }

    /**
     * {@inheritdoc}
     */
    public function setPaths(array $paths): FinderContract
    {
        $this->paths = $paths;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getHints(): array
    {
        return $this->hints;
    }

    /**
     * {@inheritdoc}
     */
    public function getExtensions(): array
    {
        return self::$extensions;
    }

    /**
     * {@inheritdoc}
     */
    public static function getDimensions(): array
    {
        return ['viserio', 'view'];
    }

    /**
     * {@inheritdoc}
     */
    public static function getMandatoryOptions(): array
    {
        return [
            'paths',
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function find(string $name): array
    {
        if (isset($this->views[$name])) {
            return $this->views[$name];
        }

        if ($this->hasHintInformation($name = \trim($name))) {
            return $this->views[$name] = $this->findNamedPathView($name);
        }

        return $this->views[$name] = $this->findInPaths($name, $this->paths);
    }

    /**
     * {@inheritdoc}
     */
    public function addLocation(string $location): FinderContract
    {
        $this->paths[] = $location;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function prependLocation(string $location): void
    {
        \array_unshift($this->paths, $location);
    }

    /**
     * {@inheritdoc}
     */
    public function addNamespace(string $namespace, $hints): FinderContract
    {
        $hints = (array) $hints;

        if (isset($this->hints[$namespace])) {
            $hints = \array_merge($this->hints[$namespace], $hints);
        }

        $this->hints[$namespace] = $hints;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function prependNamespace(string $namespace, $hints): FinderContract
    {
        $hints = (array) $hints;

        if (isset($this->hints[$namespace])) {
            $hints = \array_merge($hints, $this->hints[$namespace]);
        }

        $this->hints[$namespace] = $hints;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function addExtension(string $extension): FinderContract
    {
        if (($index = \array_search($extension, self::$extensions, true)) !== false) {
            unset(self::$extensions[$index]);
        }

        \array_unshift(self::$extensions, $extension);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function hasHintInformation(string $name): bool
    {
        return \mb_strpos($name, FinderContract::HINT_PATH_DELIMITER) > 0;
    }

    /**
     * {@inheritdoc}
     */
    public function getFilesystem(): FilesystemContract
    {
        return $this->files;
    }

    /**
     * {@inheritdoc}
     */
    public function replaceNamespace(string $namespace, $hints): FinderContract
    {
        $this->hints[$namespace] = (array) $hints;

        return $this;
    }

    /**
     * {@inheritdoc}
     *
     * @codeCoverageIgnore
     */
    public function reset(): void
    {
        $this->views = [];
    }

    /**
     * Get the path to a template with a named path.
     *
     * @param string $name
     *
     * @return array
     */
    protected function findNamedPathView(string $name): array
    {
        [$namespace, $view] = $this->getNamespaceSegments($name);

        return $this->findInPaths($view, $this->hints[$namespace]);
    }

    /**
     * Get the segments of a template with a named path.
     *
     * @param string $name
     *
     * @throws \Viserio\Component\Contract\View\Exception\InvalidArgumentException
     *
     * @return array
     */
    protected function getNamespaceSegments(string $name): array
    {
        $segments = \explode(FinderContract::HINT_PATH_DELIMITER, $name);

        if (\count($segments) !== 2) {
            throw new InvalidArgumentException(\sprintf('View [%s] has an invalid name.', $name));
        }

        if (! isset($this->hints[$segments[0]])) {
            throw new InvalidArgumentException(\sprintf('No hint path defined for [%s].', $segments[0]));
        }

        return $segments;
    }

    /**
     * Find the given view in the list of paths.
     *
     * @param string $name
     * @param array  $paths
     *
     * @throws \Viserio\Component\Contract\View\Exception\InvalidArgumentException
     *
     * @return array
     */
    protected function findInPaths(string $name, array $paths): array
    {
        foreach ($paths as $path) {
            foreach ($this->getPossibleViewFiles($name) as $fileInfos) {
                $viewPath = $path . \DIRECTORY_SEPARATOR . $fileInfos['file'];

                if ($this->files->has($viewPath)) {
                    return [
                        'path'      => $viewPath,
                        'name'      => $fileInfos['file'],
                        'extension' => $fileInfos['extension'],
                    ];
                }
            }
        }

        throw new InvalidArgumentException(\sprintf('View [%s] not found.', $name));
    }

    /**
     * Get an array of possible view files.
     *
     * @param string $name
     *
     * @return array
     */
    protected function getPossibleViewFiles(string $name): array
    {
        return \array_map(static function ($extension) use ($name) {
            return [
                'extension' => $extension,
                'file'      => \str_replace('.', \DIRECTORY_SEPARATOR, $name) . '.' . $extension,
            ];
        }, self::$extensions);
    }
}


<?php
declare(strict_types=1);
namespace Viserio\Component\Events\DataCollector;

use Closure;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\VarDumper\Caster\ClassStub;
use Viserio\Component\Contract\Events\Event as EventContract;
use Viserio\Component\Events\Traits\EventTrait;

/**
 * Some of this code has been ported from Symfony. The original
 * code is (c) Fabien Potencier <fabien@symfony.com>.
 */
class WrappedListener
{
    use EventTrait;

    /**
     * Event manager instance.
     *
     * @var null|\Viserio\Component\Events\DataCollector\TraceableEventManager
     */
    protected $eventManager;

    /**
     * Given event listener.
     *
     * @var array|\Closure|string
     */
    private $listener;

    /**
     * Is event listener called?
     *
     * @var bool
     */
    private $called = false;

    /**
     * A Stopwatch instance.
     *
     * @var \Symfony\Component\Stopwatch\Stopwatch
     */
    private $stopwatch;

    /**
     * A pretty sting info about the listener.
     *
     * @var string
     */
    private $pretty;

    /**
     * @var \Symfony\Component\VarDumper\Caster\ClassStub
     */
    private $stub;

    /**
     * Returns true if ClassStub exists or false if not.
     *
     * @var bool
     */
    private static $hasClassStub;

    /**
     * Create a new WrappedListener instance.
     *
     * @param array|\Closure|string                                              $listener
     * @param null|string                                                        $name
     * @param \Symfony\Component\Stopwatch\Stopwatch                             $stopwatch
     * @param null|\Viserio\Component\Events\DataCollector\TraceableEventManager $eventManager
     */
    public function __construct(
        $listener,
        ?string $name,
        Stopwatch $stopwatch,
        TraceableEventManager $eventManager = null
    ) {
        $this->listener     = $listener;
        $this->stopwatch    = $stopwatch;
        $this->eventManager = $eventManager;

        $this->analyzeListener($listener);

        if ($name !== null) {
            $this->name = $name;
        }

        if (self::$hasClassStub === null) {
            self::$hasClassStub = \class_exists(ClassStub::class);
        }
    }

    /**
     * @param \Viserio\Component\Contract\Events\Event $event
     *
     * @return void
     */
    public function __invoke(EventContract $event): void
    {
        $this->called = true;

        $stopWatch = $this->stopwatch->start($this->name, 'event_listener');

        \call_user_func($this->listener, $event);

        if ($stopWatch->isStarted()) {
            $stopWatch->stop();
        }

        if ($event->isPropagationStopped()) {
            $this->propagationStopped = true;
        }
    }

    /**
     * Get a pretty info string about the called event.
     *
     * @return string
     */
    public function getPretty(): string
    {
        return $this->pretty;
    }

    /**
     * Get the original listener.
     *
     * @return array|Closure|string
     */
    public function getWrappedListener()
    {
        return $this->listener;
    }

    /**
     * Was the event called?
     *
     * @return bool
     */
    public function wasCalled(): bool
    {
        return $this->called;
    }

    /**
     * Get information's about given event.
     *
     * @param string $eventName
     *
     * @return array
     */
    public function getInfo(string $eventName): array
    {
        if ($this->stub === null) {
            $this->stub = self::$hasClassStub ? new ClassStub($this->pretty . '()', $this->listener) : $this->pretty . '()';
        }

        return [
            'priority' => $this->eventManager !== null ? $this->eventManager->getListenerPriority($eventName, $this->listener) : null,
            'pretty'   => $this->pretty,
            'stub'     => $this->stub,
        ];
    }

    /**
     * @param array|\Closure|string $listener
     *
     * @return void
     */
    private function analyzeListener($listener): void
    {
        if (\is_array($listener)) {
            $this->name   = \is_object($listener[0]) ? \get_class($listener[0]) : $listener[0];
            $this->pretty = $this->name . '::' . $listener[1];
        } elseif ($listener instanceof Closure) {
            $this->pretty = $this->name = 'closure';
        } elseif (\is_string($listener)) {
            $this->pretty = $this->name = $listener;
        } else {
            $this->name   = \get_class($listener);
            $this->pretty = $this->name . '::__invoke';
        }
    }
}


<?php
declare(strict_types=1);
namespace Viserio\Bridge\Twig\NodeVisitor;

use Twig\Environment;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\FilterExpression;
use Twig\Node\Node;
use Twig\NodeVisitor\AbstractNodeVisitor;
use Viserio\Bridge\Twig\Node\TransNode;

/**
 * TranslationNodeVisitor extracts translation messages.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 * @copyright Copyright (c) 2004-2017 Fabien Potencier
 */
class TranslationNodeVisitor extends AbstractNodeVisitor
{
    /**
     * @var string
     */
    private const UNDEFINED_DOMAIN = '_undefined';

    /**
     * Enable/Disable status of node.
     *
     * @var bool
     */
    private $enabled = false;

    /**
     * Array of all given messages.
     *
     * @var array
     */
    private $messages = [];

    /**
     * Get a list of messages.
     *
     * @return array
     */
    public function getMessages(): array
    {
        return $this->messages;
    }

    /**
     * Enable node and clear messages.
     *
     * @return void
     */
    public function enable(): void
    {
        $this->enabled  = true;
        $this->messages = [];
    }

    /**
     * Disable node and clear messages.
     *
     * @return void
     */
    public function disable(): void
    {
        $this->enabled  = false;
        $this->messages = [];
    }

    /**
     * {@inheritdoc}
     */
    public function getPriority(): int
    {
        return 0;
    }

    /**
     * {@inheritdoc}
     */
    protected function doEnterNode(Node $node, Environment $env): Node
    {
        if (! $this->enabled) {
            return $node;
        }

        if ($node instanceof FilterExpression &&
            $node->getNode('node') instanceof ConstantExpression &&
            $node->getNode('filter')->getAttribute('value') === 'trans'
        ) {
            // extract constant nodes with a trans filter
            $this->messages[] = [
                $node->getNode('node')->getAttribute('value'),
                $this->getReadDomainFromArguments($node->getNode('arguments'), 1),
            ];
        } elseif ($node instanceof TransNode) {
            // extract trans nodes
            $this->messages[] = [
                $node->getNode('body')->getAttribute('data'),
                $node->hasNode('domain') ? $this->getReadDomainFromNode($node->getNode('domain')) : null,
            ];
        }

        return $node;
    }

    /**
     * {@inheritdoc}
     */
    protected function doLeaveNode(Node $node, Environment $env): Node
    {
        return $node;
    }

    /**
     * @param \Twig\Node\Node $arguments
     * @param int             $index
     *
     * @return null|string
     */
    private function getReadDomainFromArguments(Node $arguments, int $index): ?string
    {
        if ($arguments->hasNode('domain')) {
            $argument = $arguments->getNode('domain');
        } elseif ($arguments->hasNode($index)) {
            $argument = $arguments->getNode($index);
        } else {
            return null;
        }

        return $this->getReadDomainFromNode($argument);
    }

    /**
     * @param \Twig\Node\Node $node
     *
     * @return null|string
     */
    private function getReadDomainFromNode(Node $node): ?string
    {
        if ($node instanceof ConstantExpression) {
            return $node->getAttribute('value');
        }

        return self::UNDEFINED_DOMAIN;
    }
}


<?php
declare(strict_types=1);
namespace Viserio\Component\Cookie;

use Cake\Chronos\Chronos;
use Psr\Http\Message\ResponseInterface;
use Viserio\Component\Contract\Cookie\Cookie as CookieContract;
use Viserio\Component\Contract\Cookie\Exception\InvalidArgumentException;

final class ResponseCookies extends AbstractCookieCollector
{
    /**
     * Create a new cookie collection instance.
     *
     * @param array $cookies
     *
     * @throws \Viserio\Component\Contract\Cookie\Exception\InvalidArgumentException
     */
    public function __construct(array $cookies = [])
    {
        foreach ($cookies as $cookie) {
            if (! ($cookie instanceof CookieContract)) {
                throw new InvalidArgumentException(\sprintf(
                    'The object [%s] must implement [%s].',
                    \get_class($cookie),
                    CookieContract::class
                ));
            }

            $this->cookies[$cookie->getName()] = $cookie;
        }
    }

    /**
     * Creates a Cookie instance from a Set-Cookie header value.
     *
     * @param \Psr\Http\Message\ResponseInterface $response
     *
     * @throws \Viserio\Component\Contract\Cookie\Exception\InvalidArgumentException
     *
     * @return self
     */
    public static function fromResponse(ResponseInterface $response): self
    {
        return new static(\array_map(static function ($setCookieString) {
            return self::fromStringCookie($setCookieString);
        }, $response->getHeader('set-cookie')));
    }

    /**
     * Render Cookies into a response.
     *
     * @param \Psr\Http\Message\ResponseInterface $response
     *
     * @return \Psr\Http\Message\ResponseInterface
     */
    public function renderIntoSetCookieHeader(ResponseInterface $response): ResponseInterface
    {
        $response = $response->withoutHeader('set-cookie');

        foreach ($this->cookies as $cookies) {
            $response = $response->withAddedHeader('set-cookie', (string) $cookies);
        }

        return $response;
    }

    /**
     * Creates a Cookie instance from a Set-Cookie header value.
     *
     * @param string $string
     *
     * @return \Viserio\Component\Contract\Cookie\Cookie
     */
    protected static function fromStringCookie(string $string): CookieContract
    {
        $rawAttributes = self::splitOnAttributeDelimiter($string);

        [$cookieName, $cookieValue] = self::splitCookiePair(\array_shift($rawAttributes));

        $cookie = new SetCookie($cookieName);

        if (null !== $cookieValue) {
            $cookie = $cookie->withValue($cookieValue);
        }

        foreach ($rawAttributes as $value) {
            $rawAttributePair = \explode('=', $value, 2);
            $attributeKey     = $rawAttributePair[0];
            $attributeValue   = \count($rawAttributePair) > 1 ? $rawAttributePair[1] : null;
            $attributeKey     = \mb_strtolower($attributeKey);

            switch ($attributeKey) {
                case 'expires':
                    $cookie = $cookie->withExpires(new Chronos($attributeValue));

                    break;
                case 'max-age':
                    $age    = \is_numeric($attributeValue) ? (int) $attributeValue : null;
                    $cookie = $cookie->withMaxAge($age);

                    break;
                case 'domain':
                    $cookie = $cookie->withDomain($attributeValue);

                    break;
                case 'path':
                    $cookie = $cookie->withPath($attributeValue ?? '/');

                    break;
                case 'secure':
                    $cookie = $cookie->withSecure(true);

                    break;
                case 'httponly':
                    $cookie = $cookie->withHttpOnly(true);

                    break;
                case 'samesite':
                    $cookie = $cookie->withSameSite($attributeValue);

                    break;
            }
        }

        return $cookie;
    }
}


<?php
declare(strict_types=1);
namespace Viserio\Bridge\Twig\Provider;

use Interop\Container\ServiceProviderInterface;
use Psr\Container\ContainerInterface;
use Twig\Environment as TwigEnvironment;
use Twig\Extension\ProfilerExtension;
use Twig\Profiler\Profile;
use Viserio\Bridge\Twig\DataCollector\TwigDataCollector;
use Viserio\Component\Contract\OptionsResolver\ProvidesDefaultOptions as ProvidesDefaultOptionsContract;
use Viserio\Component\Contract\OptionsResolver\RequiresComponentConfig as RequiresComponentConfigContract;
use Viserio\Component\Contract\Profiler\Profiler as ProfilerContract;
use Viserio\Component\OptionsResolver\Traits\OptionsResolverTrait;

class TwigBridgeDataCollectorsServiceProvider implements
    ServiceProviderInterface,
    RequiresComponentConfigContract,
    ProvidesDefaultOptionsContract
{
    use OptionsResolverTrait;

    /**
     * {@inheritdoc}
     */
    public function getFactories(): array
    {
        return [
            Profile::class => static function (): Profile {
                return new Profile();
            },
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function getExtensions(): array
    {
        return [
            TwigEnvironment::class  => [self::class, 'extendTwigEnvironment'],
            ProfilerContract::class => [self::class, 'extendProfiler'],
        ];
    }

    /**
     * {@inheritdoc}
     */
    public static function getDimensions(): array
    {
        return ['viserio', 'profiler'];
    }

    /**
     * {@inheritdoc}
     */
    public static function getDefaultOptions(): array
    {
        return [
            'collector' => [
                'twig' => false,
            ],
        ];
    }

    /**
     * Extend viserio profiler with data collector.
     *
     * @param \Psr\Container\ContainerInterface                  $container
     * @param null|\Viserio\Component\Contract\Profiler\Profiler $profiler
     *
     * @return null|\Viserio\Component\Contract\Profiler\Profiler
     */
    public static function extendProfiler(
        ContainerInterface $container,
        ?ProfilerContract $profiler = null
    ): ?ProfilerContract {
        if ($profiler !== null) {
            $options = self::resolveOptions($container->get('config'));

            if ($options['collector']['twig'] === true) {
                $profiler->addCollector(new TwigDataCollector(
                    $container->get(Profile::class),
                    $container->get(TwigEnvironment::class)
                ));
            }
        }

        return $profiler;
    }

    /**
     * Wrap Twig Environment.
     *
     * @param \Psr\Container\ContainerInterface $container
     * @param null|\Twig\Environment            $twig
     *
     * @return null|\Twig\Environment
     */
    public static function extendTwigEnvironment(
        ContainerInterface $container,
        ?TwigEnvironment $twig = null
    ): ?TwigEnvironment {
        if ($twig !== null) {
            $options = self::resolveOptions($container->get('config'));

            if ($options['collector']['twig'] === true) {
                $twig->addExtension(new ProfilerExtension(
                    $container->get(Profile::class)
                ));
            }
        }

        return $twig;
    }
}


<?php
declare(strict_types=1);
namespace Viserio\Component\Foundation\Config\Processor;

use Psr\Container\ContainerInterface;
use Viserio\Component\Config\ParameterProcessor\AbstractParameterProcessor;
use Viserio\Component\Contract\Container\Traits\ContainerAwareTrait;
use Viserio\Component\Contract\OptionsResolver\RequiresComponentConfig as RequiresComponentConfigContract;
use Viserio\Component\Contract\OptionsResolver\RequiresMandatoryOptions as RequiresMandatoryOptionsContract;
use Viserio\Component\Contract\OptionsResolver\RequiresValidatedConfig as RequiresValidatedConfigContract;
use Viserio\Component\OptionsResolver\Traits\OptionsResolverTrait;

final class DirectoryProcessor extends AbstractParameterProcessor implements
    RequiresMandatoryOptionsContract,
    RequiresComponentConfigContract,
    RequiresValidatedConfigContract
{
    use OptionsResolverTrait;
    use ContainerAwareTrait;

    /**
     * Resolved options.
     *
     * @var array
     */
    protected $resolvedOptions = [];

    /**
     * Create a new DirectoryProcessor instance.
     *
     * @param array|\ArrayAccess                $config
     * @param \Psr\Container\ContainerInterface $container
     */
    public function __construct($config, ContainerInterface $container)
    {
        $this->resolvedOptions = self::resolveOptions($config);
        $this->container       = $container;
    }

    /**
     * {@inheritdoc}
     */
    public static function getDimensions(): array
    {
        return ['viserio', 'config', 'processor', self::getReferenceKeyword()];
    }

    /**
     * {@inheritdoc}
     */
    public static function getMandatoryOptions(): array
    {
        return ['mapper'];
    }

    /**
     * {@inheritdoc}
     */
    public static function getOptionValidators(): array
    {
        return [
            'mapper' => ['array'],
        ];
    }

    /**
     * {@inheritdoc}
     */
    public static function getReferenceKeyword(): string
    {
        return 'directory';
    }

    /**
     * {@inheritdoc}
     */
    public function process(string $data)
    {
        $parameterKey      = $this->parseParameter($data);
        $parameterKeyValue = $this->resolvedOptions['mapper'][$parameterKey] ?? null;

        if ($parameterKeyValue === null) {
            return $data;
        }

        $newValue = $this->container->has($parameterKeyValue[0]) ? $this->container->get($parameterKeyValue[0])->{$parameterKeyValue[1]}() : null;

        if ($newValue === null) {
            return $data;
        }

        return $this->replaceData(
            $data,
            $parameterKey,
            (string) $newValue
        );
    }
}


<?php
declare(strict_types=1);
namespace Viserio\Component\Profiler\DataCollector;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

class MessagesDataCollector extends AbstractDataCollector
{
    /**
     * Message name.
     *
     * @var string
     */
    protected $name;

    /**
     * Collection of all messages.
     *
     * @var array
     */
    protected $messages = [];

    /**
     * Create new messages collector.
     *
     * @param string $name
     */
    public function __construct(string $name = 'messages')
    {
        $this->name = $name;
    }

    /**
     * Returns collected messages.
     *
     * @return array
     */
    public function getMessages(): array
    {
        $messages = $this->messages;

        // sort messages by their timestamp
        \usort($messages, static function ($a, $b) {
            if ($a['time'] === $b['time']) {
                return 0;
            }

            return $a['time'] < $b['time'] ? -1 : 1;
        });

        return $messages;
    }

    /**
     * {@inheritdoc}
     */
    public function collect(ServerRequestInterface $serverRequest, ResponseInterface $response): void
    {
        $messages = $this->getMessages();

        $this->data = [
            'counted'  => \count($messages),
            'messages' => $messages,
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function getMenu(): array
    {
        return [
            'label' => 'Messages',
            'value' => $this->data['counted'],
        ];
    }

    /**
     * Adds a message.
     *
     * A message can be anything from an object to a string.
     *
     * @param mixed  $message
     * @param string $label
     *
     * @return void
     */
    public function addMessage($message, string $label = 'info'): void
    {
        if (! \is_string($message)) {
            $message = $this->cloneVar($message);
        }

        $this->messages[] = [
            'message' => \is_string($message) ? $message : $this->cloneVar($message),
            'label'   => $label,
            'time'    => \microtime(true),
        ];
    }

    /**
     * Deletes all messages.
     *
     * @return void
     */
    public function reset(): void
    {
        $this->messages = [];
    }
}


<?php
declare(strict_types=1);
namespace Viserio\Component\Events\Provider;

use Interop\Container\ServiceProviderInterface;
use Psr\Container\ContainerInterface;
use Viserio\Component\Contract\Events\EventManager as EventManagerContract;
use Viserio\Component\Events\EventManager;

class EventsServiceProvider implements ServiceProviderInterface
{
    /**
     * {@inheritdoc}
     */
    public function getFactories(): array
    {
        return [
            EventManagerContract::class => static function (): EventManager {
                return new EventManager();
            },
            EventManager::class => static function (ContainerInterface $container) {
                return $container->get(EventManagerContract::class);
            },
            'events' => static function (ContainerInterface $container) {
                return $container->get(EventManagerContract::class);
            },
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function getExtensions(): array
    {
        return [];
    }
}


<?php
declare(strict_types=1);
namespace Viserio\Component\Routing\Traits;

use Viserio\Component\Contract\Routing\Exception\RuntimeException;
use Viserio\Component\Contract\Routing\Exception\UnexpectedValueException;
use Viserio\Component\Contract\Routing\MiddlewareAware as MiddlewareAwareContract;

trait MiddlewareAwareTrait
{
    use MiddlewareValidatorTrait;

    /**
     * List of registered middleware.
     *
     * @var array
     */
    protected $middleware = [];

    /**
     * List of removed middleware.
     *
     * @var array
     */
    protected $bypassedMiddleware = [];

    /**
     * Register a short-hand name for a middleware.
     *
     * @param string                                      $name
     * @param \Psr\Http\Server\MiddlewareInterface|string $middleware
     *
     * @throws \Viserio\Component\Contract\Routing\Exception\RuntimeException         if alias exists
     * @throws \Viserio\Component\Contract\Routing\Exception\UnexpectedValueException if wrong type is given
     *
     * @return $this
     */
    public function aliasMiddleware(string $name, $middleware)
    {
        if (isset($this->middleware[$name])) {
            throw new RuntimeException(\sprintf('Alias [%s] already exists.', $name));
        }

        if (\is_string($middleware) || \is_object($middleware)) {
            $className = $this->getMiddlewareClassName($middleware);

            if (\class_exists($className)) {
                $this->validateMiddleware($className);
            }

            $this->middleware[$name] = $middleware;

            return $this;
        }

        throw new UnexpectedValueException(\sprintf('Expected string or object; received [%s].', \gettype($middleware)));
    }

    /**
     * Adds a middleware or a array of middleware to the route/controller.
     *
     * @param array|\Psr\Http\Server\MiddlewareInterface|string $middleware
     *
     * @throws \Viserio\Component\Contract\Routing\Exception\UnexpectedValueException if wrong type is given
     *
     * @return \Viserio\Component\Contract\Routing\MiddlewareAware
     */
    public function withMiddleware($middleware): MiddlewareAwareContract
    {
        $this->validateInput($middleware);

        if (\is_string($middleware) || \is_object($middleware)) {
            $className = \is_object($middleware) ? \get_class($middleware) : $middleware;

            if (\class_exists($className)) {
                $this->validateMiddleware($className);
            }

            $this->middleware[$className] = $middleware;

            return $this;
        }

        foreach ($middleware as $middleware) {
            $className = $this->getMiddlewareClassName($middleware);

            if (\class_exists($className)) {
                $this->validateMiddleware($className);
            }

            $this->middleware[$className] = $middleware;
        }

        return $this;
    }

    /**
     * Remove the given middleware from the route/controller.
     * If no middleware is passed, all middleware will be removed.
     *
     * @param null|array|string $middleware
     *
     * @throws \Viserio\Component\Contract\Routing\Exception\UnexpectedValueException if wrong type is given
     *
     * @return \Viserio\Component\Contract\Routing\MiddlewareAware
     */
    public function withoutMiddleware($middleware = null): MiddlewareAwareContract
    {
        if ($middleware === null) {
            $this->middleware = [];

            return $this;
        }

        $this->validateInput($middleware);

        if (\is_object($middleware) || \is_string($middleware)) {
            $name = \is_object($middleware) ? \get_class($middleware) : $middleware;

            $this->bypassedMiddleware[$name] = true;

            return $this;
        }

        foreach ($middleware as $name => $middleware) {
            $middleware = $this->getMiddlewareClassName($middleware);
            $name       = \is_numeric($name) ? $middleware : $name;

            $this->bypassedMiddleware[$name] = true;
        }

        return $this;
    }
}


<?php
declare(strict_types=1);
namespace Viserio\Component\WebServer\Command;

use Symfony\Component\Console\Output\OutputInterface;
use Viserio\Bridge\Monolog\Formatter\ConsoleFormatter;
use Viserio\Bridge\Monolog\Handler\ConsoleHandler;
use Viserio\Component\Console\Command\AbstractCommand;
use Viserio\Component\Contract\WebServer\Exception\RuntimeException;

final class ServerLogCommand extends AbstractCommand
{
    /**
     * {@inheritdoc}
     */
    protected static $defaultName = 'server:log';

    /**
     * {@inheritdoc}
     */
    protected $signature = 'server:log
        [-H|--host=0.0.0.0 : The hostname to listen to.]
        [-p|--port=9911 : The port to listen to.]
        [--format= : The line format.]
        [--date-format= : The date format.]
    ';

    /**
     * {@inheritdoc}
     */
    protected $description = 'Starts a log server that displays logs in real time.';

    /**
     * Colors of the background.
     *
     * @var array
     */
    private static $bgColor = ['black', 'blue', 'cyan', 'green', 'magenta', 'red', 'white', 'yellow'];

    /**
     * A ConsoleHandler instance.
     *
     * @var \Viserio\Bridge\Monolog\Handler\ConsoleHandler
     */
    private $handler;

    /**
     * {@inheritdoc}
     */
    public function handle(): int
    {
        $output        = $this->getOutput();
        $this->handler = new ConsoleHandler($output);
        $format        = $this->hasOption('format') ? $this->option('format') : ConsoleFormatter::SIMPLE_FORMAT;
        $dateFormat    = $this->hasOption('date-format') ? $this->option('date-format') : ConsoleFormatter::SIMPLE_DATE;

        $this->handler->setFormatter(new ConsoleFormatter([
            'format'      => \str_replace('\n', "\n", $format),
            'date_format' => $dateFormat,
            'colors'      => $output->isDecorated(),
            'multiline'   => OutputInterface::VERBOSITY_DEBUG <= $output->getVerbosity(),
        ]));

        $host    = $this->option('host');
        $port    = $this->option('port');
        $address = 'tcp://' . $host . ':' . $port;

        if (! $socket = \stream_socket_server($address, $errno, $errstr)) {
            throw new RuntimeException(\sprintf('Server start failed on "%s": %s %s.', $address, $errstr, $errno));
        }

        $output->success(\sprintf(
            'Server listening on %s',
            $host !== '0.0.0.0' ? $host . ':' . $port : 'all interfaces, port ' . $port
        ));

        foreach ($this->getLogs($socket) as $clientId => $message) {
            $record = \unserialize(\base64_decode($message, true));

            // Impossible to decode the message, give up.
            if ($record === false) {
                continue;
            }

            $this->displayLog($output, $clientId, $record);
        }

        return 0;
    }

    /**
     * Get collected logs.
     *
     * @param resource $socket
     *
     * @return null|\Generator
     */
    private function getLogs($socket): ?\Generator
    {
        $sockets = [\fstat($socket)['size'] => $socket];
        $write   = [];

        while (true) {
            $read = $sockets;
            \stream_select($read, $write, $write, null);

            foreach ($read as $stream) {
                $size = \fstat($stream)['size'];

                if ($socket === $stream) {
                    $stream                 = \stream_socket_accept($socket);
                    $sockets[$size]         = $stream;
                } elseif (\feof($stream)) {
                    unset($sockets[$size]);

                    \fclose($stream);
                } else {
                    yield $size => \fgets($stream);
                }
            }
        }
    }

    /**
     * Display the record as log message.
     *
     * @param OutputInterface $output
     * @param int             $clientId
     * @param array           $record
     *
     * @return void
     */
    private function displayLog(OutputInterface $output, int $clientId, array $record): void
    {
        if ($this->handler->isHandling($record)) {
            if (isset($record['log_id'])) {
                $clientId = \unpack('H*', $record['log_id'])[1];
            }

            $logBlock = \sprintf('<bg=%s> </>', self::$bgColor[$clientId % 8]);

            $output->write($logBlock);
        }

        $this->handler->handle($record);
    }
}


<?php
declare(strict_types=1);
namespace Viserio\Component\View\Engine;

use ErrorException;
use ParseError;
use Throwable;
use TypeError;
use Viserio\Component\Contract\View\Engine as EngineContract;

class PhpEngine implements EngineContract
{
    /**
     * {@inheritdoc}
     */
    public function get(array $fileInfo, array $data = []): string
    {
        $obLevel = \ob_get_level();

        \ob_start();

        // We'll evaluate the contents of the view inside a try/catch block so we can
        // clear out any stray output that might get out before an error occurs or
        // an exception is thrown. This prevents any partial views from leaking.
        \extract($data, \EXTR_PREFIX_SAME, 'narrowspark');

        try {
            require $fileInfo['path'];
        } catch (Throwable $exception) {
            $this->handleViewException(
                $this->getErrorException($exception),
                $obLevel
            );
        }

        // @codeCoverageIgnoreStart
        // Return temporary output buffer content, destroy output buffer
        return \ltrim(\ob_get_clean());
        // @codeCoverageIgnoreEnd
    }

    /**
     * Handle a view exception.
     *
     * @param \Throwable $exception
     * @param int        $obLevel
     *
     * @throws \Throwable
     */
    protected function handleViewException(Throwable $exception, int $obLevel): void
    {
        while (\ob_get_level() > $obLevel) {
            \ob_end_clean();
        }

        throw $exception;
    }

    /**
     * Get a ErrorException instance.
     *
     * @param \ParseError|\Throwable|\TypeError $exception
     *
     * @return \ErrorException
     */
    private function getErrorException($exception): ErrorException
    {
        // @codeCoverageIgnoreStart
        if ($exception instanceof ParseError) {
            $message  = 'Parse error: ' . $exception->getMessage();
            $severity = \E_PARSE;
        } elseif ($exception instanceof TypeError) {
            $message  = 'Type error: ' . $exception->getMessage();
            $severity = \E_RECOVERABLE_ERROR;
        } else {
            $message  = $exception->getMessage();
            $severity = \E_ERROR;
        }
        // @codeCoverageIgnoreEnd

        return new ErrorException(
            $message,
            $exception->getCode(),
            $severity,
            $exception->getFile(),
            $exception->getLine()
        );
    }
}


<?php
declare(strict_types=1);
namespace Viserio\Component\Console\Automatic;

use Narrowspark\Automatic\Common\ScriptExtender\PhpScriptExtender;
use Viserio\Component\Console\Application;

class CerebroScriptExtender extends PhpScriptExtender
{
    /**
     * {@inheritdoc}
     */
    public static function getType(): string
    {
        return 'cerebro-cmd';
    }

    /**
     * {@inheritdoc}
     */
    public function expand(string $cmd): string
    {
        $console = Application::cerebroBinary();

        if ($this->io->isDecorated()) {
            $console .= ' --ansi';
        }

        return parent::expand($console . ' ' . $cmd);
    }
}


<?php
declare(strict_types=1);
namespace Viserio\Component\StaticalProxy;

use RuntimeException;
use Viserio\Component\Contract\StaticalProxy\AliasLoader as AliasLoaderContract;
use Viserio\Component\StaticalProxy\Traits\ExistTrait;

class AliasLoader implements AliasLoaderContract
{
    use ExistTrait;

    /**
     * Flag to enable or disable real-time statical proxy.
     *
     * @var bool
     */
    protected $realTimeStaticalProxyActivated = false;

    /**
     * The namespace for all real-time statical proxies.
     *
     * @var string
     */
    protected $staticalProxyNamespace = 'StaticalProxy\\';

    /**
     * Path to the real-time statical proxies cache folder.
     *
     * @var null|string
     */
    private $cachePath;

    /**
     * Array of class aliases.
     *
     * @var array
     */
    private $aliases = [];

    /**
     * @var Resolver[]
     */
    private $patterns = [];

    /**
     * Array of namespaces aliases.
     *
     * @var array
     */
    private $namespaces = [];

    /**
     * Indicates if a loader has been registered.
     *
     * @var bool
     */
    private $registered = false;

    /**
     * @var array
     */
    private $resolving = [];

    /**
     * All cached resolved aliases.
     *
     * @var array
     */
    private static $cache = [];

    /**
     * Create a new AliasLoader instance.
     *
     * @param array $aliases
     */
    public function __construct(array $aliases = [])
    {
        $this->aliases = $aliases;
    }

    /**
     * Clone method.
     *
     * @return void
     *
     * @codeCoverageIgnore
     */
    private function __clone()
    {
    }

    /**
     * Set the real-time statical proxy namespace.
     *
     * @param string $namespace
     *
     * @return void
     */
    public function setStaticalProxyNamespace(string $namespace): void
    {
        $this->staticalProxyNamespace = \rtrim($namespace, '\\') . '\\';
    }

    /**
     * Get the cache path.
     *
     * @throws \RuntimeException If real-time statical proxy is active and no cache path is given
     *
     * @return null|string
     */
    public function getCachePath(): ?string
    {
        if ($this->realTimeStaticalProxyActivated === true && $this->cachePath === null) {
            throw new RuntimeException('Please provide a valid cache path.');
        }

        return $this->cachePath;
    }

    /**
     * Set the cache path.
     *
     * @param string $path
     *
     * @return void
     */
    public function setCachePath(string $path): void
    {
        $this->cachePath = \rtrim($path, \DIRECTORY_SEPARATOR);
    }

    /**
     * {@inheritdoc}
     */
    public function getAliases(): array
    {
        return $this->aliases;
    }

    /**
     * {@inheritdoc}
     */
    public function setAliases(array $aliases): void
    {
        $this->aliases = $aliases;
    }

    /**
     * {@inheritdoc}
     */
    public function isRegistered(): bool
    {
        return $this->registered;
    }

    /**
     * Enable the real-time statical proxy.
     *
     * @return void
     */
    public function enableRealTimeStaticalProxy(): void
    {
        $this->realTimeStaticalProxyActivated = true;
    }

    /**
     * {@inheritdoc}
     */
    public function load(string $alias): bool
    {
        if ($this->realTimeStaticalProxyActivated === true &&
            \mb_strpos($alias, $this->staticalProxyNamespace) === 0
        ) {
            $this->loadStaticalProxy($alias);

            return true;
        }

        // Skip recursive aliases if defined
        if (\in_array($alias, $this->resolving, true)) {
            return false;
        }

        // Set it as the resolving class for when
        // we want to block recursive resolving
        $this->resolving[] = $alias;

        if (isset(self::$cache[$alias])) {
            // If we already have the alias in the cache don't bother resolving again
            $class = self::$cache[$alias];
        } elseif ($class = $this->resolveAlias($alias)) {
            // We've got a plain alias, now we can skip the others as this
            // is the most powerful one.
        } elseif ($class = $this->resolveNamespaceAlias($alias)) {
            // We've got a namespace alias, we can skip pattern matching.
        } elseif (! $class = $this->resolvePatternAlias($alias)) {
            // Lastly we'll try to resolve it through pattern matching. This is the most
            // expensive match type. Caching is recommended if you use this.
            return false;
        }

        // Remove the resolving class
        \array_pop($this->resolving);

        if (! $this->exists($class)) {
            return false;
        }

        // Create the actual alias
        \class_alias($class, $alias);

        if (! isset(self::$cache[$alias])) {
            self::$cache[$alias] = $class;
        }

        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function alias($classes, string $alias = null): AliasLoaderContract
    {
        if (\is_array($classes)) {
            $this->aliases = \array_merge($this->aliases, $classes);

            return $this;
        }

        $this->aliases[$classes] = self::$cache[$classes] = $alias;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function removeAlias(): void
    {
        $class = \func_get_args();

        foreach ($class as $alias) {
            if (isset($this->aliases[$alias])) {
                unset($this->aliases[$alias], self::$cache[$alias]);
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function resolveAlias(string $alias)
    {
        if (
            isset($this->aliases[$alias]) &&
            $this->exists($this->aliases[$alias])
        ) {
            return $this->aliases[$alias];
        }

        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function aliasPattern($patterns, string $translation = null): void
    {
        if (! \is_array($patterns)) {
            $patterns = [$patterns => $translation];
        }

        foreach ($patterns as $patternKey => $resolver) {
            if (! $resolver instanceof Resolver) {
                $resolver = new Resolver($patternKey, $resolver);
            }

            $this->patterns[$patternKey] = $resolver;
        }
    }

    /**
     * {@inheritdoc}
     */
    public function removeAliasPattern(string $pattern, string $translation = null): void
    {
        foreach (\array_keys($this->patterns) as $patternKey) {
            if ($this->patterns[$patternKey]->matches($pattern, $translation)) {
                unset($this->patterns[$patternKey]);
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function aliasNamespace(string $class, string $alias): void
    {
        $class = \trim($class, '\\');
        $alias = \trim($alias, '\\');

        $this->namespaces[] = [$class, $alias];
    }

    /**
     * {@inheritdoc}
     */
    public function resolveNamespaceAlias(string $alias)
    {
        foreach ($this->namespaces as $namespace) {
            [$nsClass, $nsAlias] = $namespace;

            if (! $nsAlias || \mb_strpos($alias, (string) $nsAlias) === 0) {
                if ($nsAlias) {
                    $alias = \mb_substr($alias, \mb_strlen($nsAlias) + 1);
                }

                $class             = $nsClass . '\\' . $alias;
                $this->resolving[] = $class;

                if ($this->exists($class)) {
                    \array_pop($this->resolving);

                    return $class;
                }
            }
        }

        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function removeNamespaceAlias(): void
    {
        $class  = \func_get_args();
        $filter = static function ($namespace) use ($class) {
            return ! \in_array($namespace[0], $class, true);
        };

        $this->namespaces = \array_filter($this->namespaces, $filter);
    }

    /**
     * {@inheritdoc}
     */
    public function register(): void
    {
        if (! $this->registered) {
            \spl_autoload_register([$this, 'load'], true, true);

            $this->registered = true;
        }
    }

    /**
     * {@inheritdoc}
     */
    public function unregister(): void
    {
        if ($this->registered) {
            \spl_autoload_unregister([$this, 'load']);

            $this->registered = false;
        }
    }

    /**
     * Resolves pattern aliases.
     *
     * @param string $alias
     *
     * @return bool|string
     */
    protected function resolvePatternAlias(string $alias)
    {
        if (isset($this->patterns[$alias])) {
            $class = $this->patterns[$alias]->resolve($alias);

            if ($class !== false) {
                return $class;
            }
        }

        foreach ($this->patterns as $resolver) {
            $class = $resolver->resolve($alias);

            if ($class !== false) {
                return $class;
            }
        }

        return false;
    }

    /**
     * Load a real-time statical proxy for the given alias.
     *
     * @param string $alias
     *
     * @return void
     */
    protected function loadStaticalProxy(string $alias): void
    {
        require $this->ensureStaticalProxyExists($alias);
    }

    /**
     * Ensure that the given alias has an existing real-time statical proxy class.
     *
     * @param string $alias
     *
     * @return string
     */
    protected function ensureStaticalProxyExists(string $alias): string
    {
        $path = $this->getCachePath() . \DIRECTORY_SEPARATOR . 'staticalproxy-' . \sha1($alias) . '.php';

        if (\file_exists($path)) {
            return $path;
        }

        \file_put_contents(
            $path,
            $this->formatStaticalProxyStub(
                $alias,
                \file_get_contents(__DIR__ . \DIRECTORY_SEPARATOR . 'Stubs' . \DIRECTORY_SEPARATOR . 'StaticalProxy.stub')
            )
        );

        return $path;
    }

    /**
     * Format the statical proxy stub with the proper namespace and class.
     *
     * @param string $alias
     * @param string $stub
     *
     * @return string
     */
    protected function formatStaticalProxyStub(string $alias, string $stub): string
    {
        $replacements = [
            \str_replace('/', '\\', \dirname(\str_replace('\\', '/', $alias))),
            self::getClassBasename($alias),
            \mb_substr($alias, \mb_strlen($this->staticalProxyNamespace)),
        ];

        return \str_replace(
            ['DummyNamespace', 'DummyClass', 'DummyTarget'],
            $replacements,
            $stub
        );
    }

    /**
     * Get the class "basename" of the given object / class.
     *
     * @param string $class
     *
     * @return string
     */
    private static function getClassBasename(string $class): string
    {
        return \basename(\str_replace('\\', '/', $class));
    }
}


<?php
declare(strict_types=1);
namespace Viserio\Component\Foundation\DataCollector;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Viserio\Component\Contract\Profiler\PanelAware as PanelAwareContract;
use Viserio\Component\Profiler\DataCollector\AbstractDataCollector;

class FilesLoadedCollector extends AbstractDataCollector implements PanelAwareContract
{
    /**
     * All compiled and included files.
     *
     * @var array
     */
    protected $files = [];

    /**
     * All included files.
     *
     * @var array
     */
    protected $included = [];

    /**
     * Base path.
     *
     * @var string
     */
    protected $basePath;

    /**
     * Create new files loaded collector instance.
     *
     * @param string $basePath
     */
    public function __construct(string $basePath)
    {
        $this->basePath = $basePath;
    }

    /**
     * {@inheritdoc}
     */
    public function collect(ServerRequestInterface $serverRequest, ResponseInterface $response): void
    {
        // Get the files included on load.
        $included = [];

        foreach (\get_included_files() as $file) {
            // Skip the files from Profiler, they are only loaded for Debugging and confuse the output.
            if (\mb_strpos($file, 'vendor/narrowspark/framework/src/Viserio/Profiler') !== false ||
                \mb_strpos($file, 'vendor/viserio/profiler') !== false
            ) {
                continue;
            }

            $included[] = $this->stripBasePath($file);
        }

        $this->included = $included;
    }

    /**
     * {@inheritdoc}
     */
    public function getMenu(): array
    {
        return [
            'icon'  => 'ic_insert_drive_file_white_24px.svg',
            'label' => '',
            'value' => (string) \count($this->included),
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function getPanel(): string
    {
        return $this->createTable(
            $this->included,
            ['headers' => ['Files']]
        );
    }

    /**
     * Remove the base path from the paths, so they are relative to the base.
     *
     * @param string $path
     *
     * @return string
     */
    protected function stripBasePath(string $path): string
    {
        return \ltrim(\str_replace($this->basePath, '', $path), '/');
    }
}


<?php
declare(strict_types=1);
namespace Viserio\Component\View\Provider;

use Interop\Container\ServiceProviderInterface;
use Parsedown;
use ParsedownExtra;
use Psr\Container\ContainerInterface;
use Viserio\Component\Contract\Filesystem\Filesystem as FilesystemContract;
use Viserio\Component\Contract\View\Factory as FactoryContract;
use Viserio\Component\Contract\View\Finder as FinderContract;
use Viserio\Component\View\Engine\EngineResolver;
use Viserio\Component\View\Engine\FileEngine;
use Viserio\Component\View\Engine\MarkdownEngine;
use Viserio\Component\View\Engine\PhpEngine;
use Viserio\Component\View\ViewFactory;
use Viserio\Component\View\ViewFinder;

class ViewServiceProvider implements ServiceProviderInterface
{
    /**
     * {@inheritdoc}
     */
    public function getFactories(): array
    {
        return [
            EngineResolver::class  => [self::class, 'createEngineResolver'],
            'view.engine.resolver' => static function (ContainerInterface $container) {
                return $container->get(EngineResolver::class);
            },
            FinderContract::class => [self::class, 'createViewFinder'],
            ViewFinder::class     => static function (ContainerInterface $container) {
                return $container->get(FinderContract::class);
            },
            'view.finder' => static function (ContainerInterface $container) {
                return $container->get(FinderContract::class);
            },
            FactoryContract::class => [self::class, 'createViewFactory'],
            ViewFactory::class     => static function (ContainerInterface $container) {
                return $container->get(FactoryContract::class);
            },
            'view' => static function (ContainerInterface $container) {
                return $container->get(FactoryContract::class);
            },
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function getExtensions(): array
    {
        return [];
    }

    public static function createEngineResolver(ContainerInterface $container): EngineResolver
    {
        $engines = new EngineResolver();

        // Next we will register the various engines with the engines so that the
        // environment can resolve the engines it needs for various views based
        // on the extension of view files. We call a method for each engines.
        foreach (['file', 'php', 'markdown'] as $engineClass) {
            self::{'register' . \ucfirst($engineClass) . 'Engine'}($engines, $container);
        }

        return $engines;
    }

    public static function createViewFinder(ContainerInterface $container): ViewFinder
    {
        return new ViewFinder($container->get(FilesystemContract::class), $container->get('config'));
    }

    public static function createViewFactory(ContainerInterface $container): FactoryContract
    {
        $view = new ViewFactory(
            $container->get(EngineResolver::class),
            $container->get(ViewFinder::class)
        );

        $view->share('app', $container);

        return $view;
    }

    /**
     * Register the PHP engine implementation.
     *
     * @param \Viserio\Component\View\Engine\EngineResolver $engines
     * @param \Psr\Container\ContainerInterface             $container
     *
     * @return void
     */
    protected static function registerPhpEngine(EngineResolver $engines, ContainerInterface $container): void
    {
        $engines->register('php', static function () {
            return new PhpEngine();
        });
    }

    /**
     * Register the File engine implementation.
     *
     * @param \Viserio\Component\View\Engine\EngineResolver $engines
     * @param \Psr\Container\ContainerInterface             $container
     *
     * @return void
     */
    protected static function registerFileEngine(EngineResolver $engines, ContainerInterface $container): void
    {
        $engines->register('file', static function () {
            return new FileEngine();
        });
    }

    /**
     * Register the Markdown engine implementation.
     *
     * @param \Viserio\Component\View\Engine\EngineResolver $engines
     * @param \Psr\Container\ContainerInterface             $container
     *
     * @return void
     */
    protected static function registerMarkdownEngine(EngineResolver $engines, ContainerInterface $container): void
    {
        $markdown = null;

        if ($container->has(ParsedownExtra::class)) {
            $markdown = $container->get(ParsedownExtra::class);
        } elseif ($container->has(Parsedown::class)) {
            $markdown = $container->get(Parsedown::class);
        }

        $engines->register('md', static function () use ($markdown) {
            return new MarkdownEngine($markdown);
        });
    }
}


<?php
declare(strict_types=1);
namespace Viserio\Component\Parser\Dumper;

use Viserio\Component\Contract\Parser\Dumper as DumperContract;
use Viserio\Component\Contract\Parser\Exception\DumpException;

class PoDumper implements DumperContract
{
    /**
     * @var array
     */
    private const LINE_ENDINGS = ['unix' => "\n", 'win' => "\r\n"];

    /**
     * Returns configured line ending (option 'line-ending' ['win', 'unix']).
     *
     * @var string
     */
    private $eol;

    /**
     * Create a new po dumper.
     *
     * @param string $eol
     *
     * @throws \Viserio\Component\Contract\Parser\Exception\DumpException
     */
    public function __construct(string $eol = 'unix')
    {
        if (! \array_key_exists($eol, self::LINE_ENDINGS)) {
            throw new DumpException('Only [unix] and [win] eol are supported.');
        }

        $this->eol = self::LINE_ENDINGS[$eol];
    }

    /**
     * {@inheritdoc}
     *
     * array[]
     *     ['headers']        array  If a multi-line header is provided
     *                               than the value is a array else a string (optional)
     *     array[]
     *         ['msgid']        array  ID of the message.
     *         ['msgstr']       array  Message translation.
     *         ['msgctxt']      array  Message context.                      (optional)
     *         ['tcomment']     array  Comment from translator.              (optional)
     *         ['ccomment']     array  Extracted comments from code.         (optional)
     *         ['references']   array  Location of string in code.           (optional)
     *         ['obsolete']     bool   Is the message obsolete?              (optional)
     *         ['fuzzy']        bool   Is the message "fuzzy"?               (optional)
     *         ['flags']        array  Flags of the entry.                   (optional)
     *         ['previous']     array                                        (optional)
     *             ['msgid']    string This is a previous string
     *             ['msgstr']   string This is a previous translation string
     *         ['msgid_plural'] array  The plural string                     (optional)
     *         ['msgstr[0]']    array  The string when the number is equal
     *         ['msgstr[1]']    array  The string when the number is equal
     */
    public function dump(array $data): string
    {
        $output = '';

        [$data, $output] = $this->addHeaderToOutput($data, $output);

        $entriesCount = \count($data);
        $counter      = 0;

        foreach ($data as $entry) {
            [$entry, $output] = $this->addPreviousToOutput($entry, $output);
            [$entry, $output] = $this->addTCommentToOutput($entry, $output);
            [$entry, $output] = $this->addCcommentToOutput($entry, $output);
            [$entry, $output] = $this->addReferencesToOutput($entry, $output);
            [$entry, $output] = $this->addFlagsToOutput($entry, $output);

            if (isset($entry['@'])) {
                $output .= '#@ ' . $entry['@'] . $this->eol;
            }

            if (isset($entry['msgctxt']) && \count($entry['msgctxt']) !== 0) {
                $output .= 'msgctxt ' . $this->cleanExport($entry['msgctxt'][0]) . $this->eol;
            }

            $isObsolete = isset($entry['obsolete']) && $entry['obsolete'];
            $isPlural   = isset($entry['msgid_plural']);

            if ($isObsolete) {
                $output .= '#~ ';
            }

            [$entry, $output] = $this->addMsgidToOutput($entry, $output, $isObsolete);
            [$entry, $output] = $this->addMsgidPluralToOutput($entry, $output);

            $output = $this->addMsgstrToOutput($entry, $isPlural, $output, $isObsolete);

            $counter++;

            // Avoid inserting an extra newline at end of file
            if ($counter < $entriesCount) {
                $output .= $this->eol;
            }
        }

        return $output;
    }

    /**
     * Prepares a string to be outputed into a file.
     *
     * @param string $string the string to be converted
     *
     * @return string
     */
    protected function cleanExport(string $string): string
    {
        $replaces = [
            '\\' => '\\\\',
            '"'  => '\"',
            "\t" => '\t',
        ];

        $string = \str_replace(\array_keys($replaces), \array_values($replaces), $string);
        $po     = '"' . \implode('$' . "\n" . '"' . $this->eol . '"', \explode($this->eol, $string)) . '"';

        // remove empty strings
        return \str_replace($this->eol . '""', '', $po);
    }

    /**
     * Adds tcomment to the output.
     *
     * @param array  $entry
     * @param string $output
     *
     * @return array
     */
    private function addTCommentToOutput(array $entry, string $output): array
    {
        if (isset($entry['tcomment']) && \count($entry['tcomment']) !== 0) {
            foreach ($entry['tcomment'] as $comment) {
                $output .= '# ' . $comment . $this->eol;
            }
        }

        return [$entry, $output];
    }

    /**
     * Adds ccomment to the output.
     *
     * @param array  $entry
     * @param string $output
     *
     * @return array
     */
    private function addCcommentToOutput($entry, string $output): array
    {
        if (isset($entry['ccomment']) && \count($entry['ccomment']) !== 0) {
            foreach ($entry['ccomment'] as $comment) {
                $output .= '#. ' . $comment . $this->eol;
            }
        }

        return [$entry, $output];
    }

    /**
     * Adds reference to the output.
     *
     * @param array  $entry
     * @param string $output
     *
     * @return array
     */
    private function addReferencesToOutput(array $entry, string $output): array
    {
        if (isset($entry['references']) && \count($entry['references']) !== 0) {
            foreach ($entry['references'] as $ref => $value) {
                $output .= '#: ' . \str_replace(['{', '}'], '', $ref) . $this->eol;
            }
        }

        return [$entry, $output];
    }

    /**
     * Adds flags infos to the output.
     *
     * @param array  $entry
     * @param string $output
     *
     * @return array
     */
    private function addFlagsToOutput(array $entry, string $output): array
    {
        if (isset($entry['flags']) && \count($entry['flags']) !== 0) {
            $output .= '#, ' . \implode(', ', $entry['flags']) . $this->eol;
        }

        return [$entry, $output];
    }

    /**
     * Adds previous info to the output.
     *
     * @param array  $entry
     * @param string $output
     *
     * @return array
     */
    private function addPreviousToOutput(array $entry, string $output): array
    {
        if (isset($entry['previous']) && \count($entry['previous']) !== 0) {
            foreach ((array) $entry['previous'] as $key => $value) {
                if (\is_string($value)) {
                    $output .= '#| ' . $key . ' ' . $this->cleanExport($value) . $this->eol;
                } elseif (\is_array($value) && \count($value) > 0) {
                    foreach ($value as $line) {
                        $output .= '#| ' . $key . ' ' . $this->cleanExport($line) . $this->eol;
                    }
                }
            }
        }

        return [$entry, $output];
    }

    /**
     * Adds msgid to the output.
     *
     * @param array  $entry
     * @param string $output
     * @param bool   $isObsolete
     *
     * @throws \Viserio\Component\Contract\Parser\Exception\DumpException
     *
     * @return array
     */
    private function addMsgidToOutput(array $entry, string $output, bool $isObsolete): array
    {
        if (isset($entry['msgid'])) {
            // Special clean for msgid
            if (\is_string($entry['msgid'])) {
                $msgid = \explode($this->eol, $entry['msgid']);
            } elseif (\is_array($entry['msgid']) && \count($entry['msgid']) !== 0) {
                $msgid = $entry['msgid'];
            } else {
                throw new DumpException('msgid not string or array.');
            }

            $output .= 'msgid ';

            foreach ((array) $msgid as $i => $id) {
                if ($i > 0 && $isObsolete) {
                    $output .= '#~ ';
                }

                $output .= $this->cleanExport($id) . $this->eol;
            }
        }

        return [$entry, $output];
    }

    /**
     * Add msgid_plural to the output.
     *
     * @param array  $entry
     * @param string $output
     *
     * @throws \Viserio\Component\Contract\Parser\Exception\DumpException
     *
     * @return array
     */
    private function addMsgidPluralToOutput(array $entry, string $output): array
    {
        if (isset($entry['msgid_plural'])) {
            // Special clean for msgid_plural
            if (\is_string($entry['msgid_plural'])) {
                $msgidPlural = \explode($this->eol, $entry['msgid_plural']);
            } elseif (\is_array($entry['msgid_plural']) && \count($entry['msgid_plural']) !== 0) {
                $msgidPlural = $entry['msgid_plural'];
            } else {
                throw new DumpException('msgid_plural not string or array.');
            }

            $output .= 'msgid_plural ';

            foreach ((array) $msgidPlural as $plural) {
                $output .= $this->cleanExport($plural) . $this->eol;
            }
        }

        return [$entry, $output];
    }

    /**
     * Adds key with msgstr to the output.
     *
     * @param array  $entry
     * @param bool   $isPlural
     * @param string $output
     * @param bool   $isObsolete
     *
     * @return string
     */
    private function addMsgstrToOutput(array $entry, bool $isPlural, string $output, bool $isObsolete): string
    {
        // checks if there is a key starting with msgstr
        if (\count(\preg_grep('/^msgstr/', \array_keys($entry))) !== 0) {
            if ($isPlural) {
                $noTranslation = true;

                foreach ($entry as $key => $value) {
                    if (\mb_strpos($key, 'msgstr[') === false) {
                        continue;
                    }

                    $output .= $key . ' ';
                    $noTranslation = false;

                    foreach ($value as $i => $t) {
                        $output .= $this->cleanExport($t) . $this->eol;
                    }
                }

                if ($noTranslation) {
                    $output .= 'msgstr[0] ' . $this->cleanExport('') . $this->eol;
                    $output .= 'msgstr[1] ' . $this->cleanExport('') . $this->eol;
                }
            } else {
                foreach ((array) $entry['msgstr'] as $i => $t) {
                    if ($isObsolete) {
                        $output .= '#~ ';
                    }

                    if ($i === 0) {
                        $output .= 'msgstr ';
                    }

                    $output .= $this->cleanExport($t) . $this->eol;
                }
            }
        }

        return $output;
    }

    /**
     * Adds a header to the output.
     *
     * @param array  $data
     * @param string $output
     *
     * @throws \Viserio\Component\Contract\Parser\Exception\DumpException
     *
     * @return array
     */
    private function addHeaderToOutput(array $data, string $output): array
    {
        if (isset($data['headers']) && \count($data['headers']) !== 0) {
            $output .= 'msgid ""' . $this->eol;
            $output .= 'msgstr ""' . $this->eol;

            foreach ($data['headers'] as $key => $value) {
                if (\is_array($value)) {
                    $first = true;

                    foreach ($value as $h) {
                        if ($first) {
                            $first = false;
                            $output .= \sprintf('"%s: %s"', $key, $h) . $this->eol;
                        } else {
                            $output .= \sprintf('"%s\n"', $h) . $this->eol;
                        }
                    }
                } else {
                    $output .= \sprintf('"%s: %s\n"', $key, $value) . $this->eol;
                }
            }

            unset($data['headers']);

            $output .= $this->eol;
        }

        return [$data, $output];
    }
}


<?php
declare(strict_types=1);
namespace Viserio\Component\Pagination\Presenter;

use Viserio\Component\Contract\Pagination\Paginator as PaginatorContract;
use Viserio\Component\Contract\Pagination\Presenter as PresenterContract;

class SemanticUi implements PresenterContract
{
    /**
     * Paginator instance.
     *
     * @var \Viserio\Component\Contract\Pagination\Paginator
     */
    protected $paginator;

    /**
     * Create a new semantic-ui presenter.
     *
     * @param \Viserio\Component\Contract\Pagination\Paginator $paginator
     */
    public function __construct(PaginatorContract $paginator)
    {
        $this->paginator = $paginator;
    }

    /**
     * {@inheritdoc}
     */
    public function render(): string
    {
        $paginator = $this->paginator;

        if ($paginator->hasPages()) {
            $pagination = '
';

            // Previous Page Link
            if ($paginator->onFirstPage()) {
                $pagination .= '';
            } else {
                $pagination .= 'getPreviousPageUrl() . '" rel="prev">';
            }

            if (\method_exists($paginator, 'getElements')) {
                $this->getPaginationsLinks($paginator->getElements(), $pagination);
            }

            // Next Page Link
            if ($paginator->hasMorePages()) {
                $pagination .= 'getNextPageUrl() . '" rel="next">';
            } else {
                $pagination .= '';
            }

            $pagination .= '
';

            return $pagination;
        }

        return '';
    }

    /**
     * Get all paginations page links.
     *
     * @param array  $items
     * @param string $pagination
     */
    private function getPaginationsLinks(array $items, $pagination): void
    {
        foreach ($items as $item) {
            if (\is_string($item)) {
                $pagination .= '' . $item . '';
            }

            // Array Of Links
            if (\is_array($item)) {
                foreach ($item as $page => $url) {
                    if ($this->paginator->getCurrentPage() === $page) {
                        $pagination .= '' . $page . '';
                    } else {
                        $pagination .= '' . $page . '';
                    }
                }
            }
        }
    }
}


<?php
declare(strict_types=1);
namespace Viserio\Component\Filesystem\Cache;

use League\Flysystem\Cached\CacheInterface;
use League\Flysystem\Cached\Storage\Adapter;
use League\Flysystem\Cached\Storage\Psr6Cache;
use Viserio\Component\Contract\Cache\Manager as CacheManagerContract;
use Viserio\Component\Contract\Filesystem\Exception\InvalidArgumentException;
use Viserio\Component\Filesystem\FilesystemManager;

class CachedFactory
{
    /**
     * Instance of FilesystemManager.
     *
     * @var \Viserio\Component\Filesystem\FilesystemManager
     */
    protected $manager;

    /**
     * Instance of CacheManager.
     *
     * @var null|\Viserio\Component\Contract\Cache\Manager
     */
    protected $cacheManager;

    /**
     * Create a new cached factory instance.
     *
     * @param \Viserio\Component\Filesystem\FilesystemManager $manager
     * @param null|\Viserio\Component\Contract\Cache\Manager  $cacheManager
     */
    public function __construct(FilesystemManager $manager, CacheManagerContract $cacheManager = null)
    {
        $this->manager      = $manager;
        $this->cacheManager = $cacheManager;
    }

    /**
     * Establish a cache connection.
     *
     * @param array $config
     *
     * @throws \Viserio\Component\Contract\Filesystem\Exception\InvalidArgumentException
     *
     * @return \League\Flysystem\Cached\CacheInterface
     */
    public function getConnection(array $config): CacheInterface
    {
        if (! isset($config['cache']['driver'])) {
            throw new InvalidArgumentException('A driver must be specified.');
        }

        return $this->createConnector($config);
    }

    /**
     * Create a connector instance based on the configuration.
     *
     * @param array $config
     *
     * @throws \InvalidArgumentException
     *
     * @return \League\Flysystem\Cached\CacheInterface
     */
    protected function createConnector(array $config): CacheInterface
    {
        $cacheConfig = $config['cache'];

        if (($cache = $this->cacheManager) !== null) {
            if ($cache->hasDriver($cacheConfig['driver'])) {
                return new Psr6Cache(
                    $cache->getDriver($cacheConfig['driver']),
                    $cacheConfig['key'],
                    $cacheConfig['expire']
                );
            }
        }

        if ($this->manager->hasConnection($cacheConfig['driver'])) {
            return new Adapter(
                $this->manager->createConnection($config),
                $cacheConfig['key'],
                $cacheConfig['expire']
            );
        }

        throw new InvalidArgumentException(\sprintf('Unsupported driver [%s].', $cacheConfig['driver']));
    }
}


<?php
declare(strict_types=1);
namespace Viserio\Component\Manager;

use Viserio\Component\Contract\Container\Traits\ContainerAwareTrait;
use Viserio\Component\Contract\Manager\Manager as ManagerContract;
use Viserio\Component\Contract\OptionsResolver\RequiresComponentConfig as RequiresComponentConfigContract;
use Viserio\Component\Contract\OptionsResolver\RequiresMandatoryOptions as RequiresMandatoryOptionsContract;
use Viserio\Component\Manager\Traits\ManagerTrait;
use Viserio\Component\OptionsResolver\Traits\OptionsResolverTrait;

abstract class AbstractManager implements
    RequiresComponentConfigContract,
    RequiresMandatoryOptionsContract,
    ManagerContract
{
    use ContainerAwareTrait;
    use OptionsResolverTrait;
    use ManagerTrait;

    /**
     * Default name for the drivers config.
     *
     * @var string
     */
    protected const CONFIG_LIST_NAME = 'drivers';

    /**
     * The array of created "drivers".
     *
     * @var array
     */
    protected $drivers = [];

    /**
     * Create a new manager instance.
     *
     * @param array|\ArrayAccess $config
     */
    public function __construct($config)
    {
        $this->resolvedOptions = static::resolveOptions($config);
    }

    /**
     * Dynamically call the default driver instance.
     *
     * @param string $method
     * @param array  $parameters
     *
     * @return mixed
     */
    public function __call(string $method, array $parameters)
    {
        return $this->getDriver()->{$method}(...$parameters);
    }

    /**
     * {@inheritdoc}
     */
    public function getDrivers(): array
    {
        return $this->drivers;
    }

    /**
     * {@inheritdoc}
     */
    public function getDefaultDriver(): string
    {
        return $this->resolvedOptions['default'];
    }

    /**
     * {@inheritdoc}
     */
    public function setDefaultDriver(string $name): void
    {
        $this->resolvedOptions['default'] = $name;
    }

    /**
     * {@inheritdoc}
     */
    public function getDriver(?string $driver = null)
    {
        $driver = $driver ?? $this->getDefaultDriver();

        // If the given driver has not been created before, we will create the instances
        // here and cache it so we can return it next time very quickly. If there is
        // already a driver created by this name, we'll just return that instance.
        if (! isset($this->drivers[$driver])) {
            $this->drivers[$driver] = $this->createDriver(
                $this->getDriverConfig($driver)
            );
        }

        return $this->drivers[$driver];
    }

    /**
     * {@inheritdoc}
     */
    public function hasDriver(string $driver): bool
    {
        $method = 'create' . static::studly($driver) . 'Driver';

        return \method_exists($this, $method) || isset($this->extensions[$driver]);
    }

    /**
     * {@inheritdoc}
     */
    public function getDriverConfig(string $name): array
    {
        $name = $name ?? $this->getDefaultDriver();

        return $this->getConfigFromName($name);
    }

    /**
     * {@inheritdoc}
     */
    public function createDriver(array $config)
    {
        $method = 'create' . static::studly($config['name']) . 'Driver';

        return $this->create($config, $method, 'Driver [%s] is not supported.');
    }
}


<?php
declare(strict_types=1);
namespace Viserio\Component\Session\Handler;

use Cake\Chronos\Chronos;

class FileSessionHandler extends AbstractSessionHandler
{
    /**
     * Get the file extension.
     *
     * @var string
     */
    public const FILE_EXTENSION = 'sess';

    /**
     * The path where sessions should be stored.
     *
     * @var string
     */
    private $path;

    /**
     * The number of seconds the session should be valid.
     *
     * @var int
     */
    private $lifetime;

    /**
     * Create a new file driven handler instance.
     *
     * @param string $path
     * @param int    $lifetime The session lifetime in seconds
     */
    public function __construct(string $path, int $lifetime)
    {
        $this->path     = $path;
        $this->lifetime = $lifetime;
    }

    /**
     * {@inheritdoc}
     */
    public function close(): bool
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function gc($maxlifetime): bool
    {
        $files = \array_filter(
            \glob($this->path . \DIRECTORY_SEPARATOR . '*.' . self::FILE_EXTENSION, \GLOB_BRACE),
            'is_file'
        );
        $boolArray = [];

        foreach ($files as $filePath) {
            $filePath = $filePath;

            if (\file_exists($filePath) && (int) \filemtime($filePath) + $maxlifetime < \time()) {
                $boolArray[] = @\unlink($filePath);
            }
        }

        return ! \in_array(false, $boolArray, true);
    }

    /**
     * {@inheritdoc}
     */
    public function updateTimestamp($sessionId, $data): bool
    {
        // touch wont work on windows.
        return \touch(
            $this->path . \DIRECTORY_SEPARATOR . $sessionId . '.' . self::FILE_EXTENSION,
            Chronos::now()->addSeconds($this->lifetime)->getTimestamp()
        );
    }

    /**
     * {@inheritdoc}
     */
    protected function doRead($sessionId): string
    {
        $filePath = $this->path . \DIRECTORY_SEPARATOR . $sessionId . '.' . self::FILE_EXTENSION;

        if (\file_exists($filePath)) {
            $timestamp = Chronos::now()->subSeconds($this->lifetime)->getTimestamp();

            if (\filemtime($filePath) >= $timestamp) {
                return (string) \file_get_contents($filePath);
            }
        }

        return '';
    }

    /**
     * {@inheritdoc}
     */
    protected function doWrite($sessionId, $sessionData): bool
    {
        return \is_int(\file_put_contents(
            $this->path . \DIRECTORY_SEPARATOR . $sessionId . '.' . self::FILE_EXTENSION,
            $sessionData,
            \LOCK_EX
        ));
    }

    /**
     * {@inheritdoc}
     */
    protected function doDestroy($sessionId): bool
    {
        return @\unlink($this->path . \DIRECTORY_SEPARATOR . $sessionId . '.' . self::FILE_EXTENSION);
    }
}


<?php
declare(strict_types=1);
namespace Viserio\Component\View\Middleware;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Viserio\Component\Contract\View\Factory as FactoryContract;

class ShareErrorsFromSessionMiddleware implements MiddlewareInterface
{
    /**
     * The view factory implementation.
     *
     * @var \Viserio\Component\Contract\View\Factory
     */
    protected $view;

    /**
     * Create a new error binder instance.
     *
     * @param \Viserio\Component\Contract\View\Factory $view
     */
    public function __construct(FactoryContract $view)
    {
        $this->view = $view;
    }

    /**
     * {@inheritdoc}
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        if (($session = $request->getAttribute('session')) !== null) {
            $this->view->share(
                'errors',
                $session->get('errors', [])
            );
        }

        return $handler->handle($request);
    }
}