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

use Viserio\Component\StaticalProxy\Traits\ExistTrait;

final class Resolver
{
    use ExistTrait;

    /**
     * @var string
     */
    protected $regex;

    /**
     * @var string
     */
    protected $pattern;

    /**
     * @var callable|string
     */
    protected $translation;

    /**
     * @var bool
     */
    protected $active = false;

    /**
     * Create a new Resolver instance.
     *
     * @param string          $pattern
     * @param callable|string $translation
     */
    public function __construct(string $pattern, $translation)
    {
        $regex             = \preg_quote($pattern, '#');
        $this->regex       = '#^' . \str_replace('\\*', '(.*)', $regex) . '$#uD';
        $this->pattern     = $pattern;
        $this->translation = $translation;
    }

    /**
     * Resolves an alias.
     *
     * @param string $alias
     *
     * @return null|string
     */
    public function resolve(string $alias): ?string
    {
        // Check wether the alias matches the pattern
        if (\preg_match($this->regex, $alias, $matches) !== 1) {
            return null;
        }

        // Get the translation
        $translation = $this->translation;

        if (\mb_strpos($translation, '$') === false) {
            $class = $translation;
        } else {
            // Make sure namespace seperators are escaped
            $translation = \str_replace('\\', '\\\\', $translation);
            // Resolve the replacement
            $class = \preg_replace($this->regex, $translation, $alias);
        }

        // Check wether the class exists
        if ($class && $this->exists($class, true)) {
            return $class;
        }

        return null;
    }

    /**
     * Checks whether the resolver matches a given pattern and optional translation.
     *
     * @param string      $pattern
     * @param null|string $translation
     *
     * @return bool
     */
    public function matches(string $pattern, string $translation = null): bool
    {
        return $this->pattern === $pattern && (! $translation || $translation === $this->translation);
    }
}


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

use Interop\Container\ServiceProviderInterface;
use Psr\Container\ContainerInterface;
use Twig\Environment as TwigEnvironment;
use Twig\Lexer;
use Twig\Loader\ArrayLoader;
use Twig\Loader\ChainLoader;
use Twig\Loader\LoaderInterface;
use Viserio\Component\Contract\OptionsResolver\RequiresComponentConfig as RequiresComponentConfigContract;
use Viserio\Component\Contract\OptionsResolver\RequiresMandatoryOptions as RequiresMandatoryOptionsContract;
use Viserio\Component\Contract\View\Factory as FactoryContract;
use Viserio\Component\Contract\View\Finder as FinderContract;
use Viserio\Component\OptionsResolver\Traits\OptionsResolverTrait;
use Viserio\Component\View\Engine\EngineResolver;
use Viserio\Provider\Twig\Engine\TwigEngine;
use Viserio\Provider\Twig\Loader as TwigLoader;

class TwigServiceProvider implements
    ServiceProviderInterface,
    RequiresComponentConfigContract,
    RequiresMandatoryOptionsContract
{
    use OptionsResolverTrait;

    /**
     * {@inheritdoc}
     */
    public function getFactories(): array
    {
        return [
            LoaderInterface::class => [self::class, 'createTwigLoader'],
            TwigLoader::class      => static function (ContainerInterface $container) {
                return $container->get(LoaderInterface::class);
            },
            TwigEnvironment::class => [self::class, 'createTwigEnvironment'],
            TwigEngine::class      => [self::class, 'createTwigEngine'],
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function getExtensions(): array
    {
        return [
            FactoryContract::class => [self::class, 'extendViewFactory'],
            EngineResolver::class  => [self::class, 'extendEngineResolver'],
        ];
    }

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

    /**
     * {@inheritdoc}
     */
    public static function getMandatoryOptions(): array
    {
        return [
            'paths',
            'engines' => [
                'twig' => [
                    'options' => [
                        'debug',
                    ],
                ],
            ],
        ];
    }

    /**
     * Create a new twig engine instance.
     *
     * @param \Psr\Container\ContainerInterface $container
     *
     * @return \Viserio\Provider\Twig\Engine\TwigEngine
     */
    public static function createTwigEngine(ContainerInterface $container): TwigEngine
    {
        $engine = new TwigEngine($container->get(TwigEnvironment::class), $container->get('config'));
        $engine->setContainer($container);

        return $engine;
    }

    /**
     * Extend ViewFactory.
     *
     * @param \Psr\Container\ContainerInterface             $container
     * @param null|\Viserio\Component\Contract\View\Factory $view
     *
     * @return null|\Viserio\Component\Contract\View\Factory
     */
    public static function extendViewFactory(
        ContainerInterface $container,
        ?FactoryContract $view = null
    ): ?FactoryContract {
        if ($view !== null) {
            // @var FactoryContract $view
            $view->addExtension('twig', 'twig');
        }

        return $view;
    }

    /**
     * Extend EngineResolver with twig extension.
     *
     * @param \Psr\Container\ContainerInterface                  $container
     * @param null|\Viserio\Component\View\Engine\EngineResolver $engines
     *
     * @return null|\Viserio\Component\View\Engine\EngineResolver
     */
    public static function extendEngineResolver(
        ContainerInterface $container,
        ?EngineResolver $engines = null
    ): ?EngineResolver {
        if ($engines !== null) {
            // @var EngineResolver $engines
            $engines->register('twig', static function () use ($container) {
                return $container->get(TwigEngine::class);
            });
        }

        return $engines;
    }

    /**
     * Create a twig environment.
     *
     * @param \Psr\Container\ContainerInterface $container
     *
     * @return \Twig\Environment
     */
    public static function createTwigEnvironment(ContainerInterface $container): TwigEnvironment
    {
        $options     = self::resolveOptions($container->get('config'));
        $twigOptions = $options['engines']['twig']['options'];

        $twig = new TwigEnvironment(
            $container->get(LoaderInterface::class),
            $twigOptions
        );

        if ($container->has(Lexer::class)) {
            $twig->setLexer($container->get(Lexer::class));
        }

        return $twig;
    }

    /**
     * Create a twig bridge loader.
     *
     * @param \Psr\Container\ContainerInterface $container
     *
     * @return \Twig\Loader\LoaderInterface
     */
    public static function createTwigLoader(ContainerInterface $container): LoaderInterface
    {
        $options = self::resolveOptions($container->get('config'));

        $loaders     = [];
        $twigOptions = $options['engines']['twig'];
        $loader      = new TwigLoader($container->get(FinderContract::class));

        if (isset($twigOptions['file_extension'])) {
            $loader->setExtension($twigOptions['file_extension']);
        }

        $loaders[] = $loader;

        if (isset($twigOptions['templates']) && \is_array($twigOptions['templates'])) {
            $loaders[] = new ArrayLoader($twigOptions['templates']);
        }

        if (isset($twigOptions['loaders']) && \is_array($twigOptions['loaders'])) {
            $loaders = \array_merge($loaders, $twigOptions['loaders']);
        }

        return new ChainLoader($loaders);
    }
}


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

use Interop\Container\ServiceProviderInterface;
use Psr\Container\ContainerInterface;
use Swift_Mailer;
use Viserio\Component\Contract\Profiler\Profiler as ProfilerContract;
use Viserio\Component\Profiler\DataCollector\Bridge\SwiftMailDataCollector;

class ProfilerSwiftMailerBridgeServiceProvider implements ServiceProviderInterface
{
    /**
     * {@inheritdoc}
     */
    public function getFactories(): array
    {
        return [];
    }

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

    /**
     * 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) {
            $profiler->addCollector(new SwiftMailDataCollector(
                $container->get(Swift_Mailer::class)
            ));
        }

        return $profiler;
    }
}


<?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 SimplePagination implements PresenterContract
{
    /**
     * Paginator instance.
     *
     * @var \Viserio\Component\Contract\Pagination\Paginator
     */
    protected $paginator;

    /**
     * Create a new Simple pagination 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">«
';
            }

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

            $pagination .= '
';

            return $pagination;
        }

        return '';
    }
}


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

use Viserio\Component\Console\Command\AbstractCommand;
use Viserio\Component\WebServer\WebServer;

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

    /**
     * {@inheritdoc}
     */
    protected $signature = 'server:stop
        [--pidfile= : PID file.]
    ';

    /**
     * {@inheritdoc}
     */
    protected $description = 'Stops the local web server that was started with the server:start command.';

    /**
     * {@inheritdoc}
     */
    public function handle(): int
    {
        try {
            WebServer::stop($this->option('pidfile'));

            $this->getOutput()->success('Stopped the web server.');

            return 0;
        } catch (\Exception $exception) {
            $this->error($exception->getMessage());

            return 1;
        }
    }
}


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

use Respect\Validation\Exceptions\NestedValidationException;
use Respect\Validation\Validator as RespectValidator;
use Viserio\Component\Contract\Translation\Traits\TranslatorAwareTrait;
use Viserio\Component\Contract\Validation\Exception\InvalidArgumentException;
use Viserio\Component\Contract\Validation\Validator as ValidatorContract;

class Validator implements ValidatorContract
{
    use TranslatorAwareTrait;

    /**
     * The failed validation rules.
     *
     * @var array
     */
    protected $failedRules = [];

    /**
     * The valid validation rules.
     *
     * @var array
     */
    protected $validRules = [];

    /**
     * Create new validator instance.
     */
    public function __construct()
    {
        RespectValidator::with('Viserio\\Component\\Validation\\Rules');
    }

    /**
     * Add your own rule's namespace.
     *
     * @param string $namespace
     * @param bool   $overwrite
     *
     * @return void
     *
     * @codeCoverageIgnore
     */
    public function with(string $namespace, bool $overwrite = false): void
    {
        RespectValidator::with($namespace, $overwrite);
    }

    /**
     * {@inheritdoc}
     */
    public function validate(array $data, array $rules): ValidatorContract
    {
        $preparedData = $this->parseData($data);

        foreach ($rules as $fieldName => $fieldRules) {
            if ($fieldRules instanceof RespectValidator) {
                $rule = $fieldRules;
            } else {
                $rule = $this->createRule($fieldRules);
            }

            $data = $preparedData[$fieldName] ?? $preparedData;

            try {
                if (\is_array($data)) {
                    foreach ($data as $value) {
                        $rule->setName(\ucfirst($fieldName))->assert($value);
                    }
                } else {
                    $rule->setName(\ucfirst($fieldName))->assert($data);
                }

                $this->validRules[$fieldName] = true;
            } catch (NestedValidationException $exception) {
                $this->failedRules[$fieldName] = $exception->getMessages();
            }
        }

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function passes(): bool
    {
        return \count($this->failedRules) === 0;
    }

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

    /**
     * {@inheritdoc}
     */
    public function fails(): bool
    {
        return ! $this->passes();
    }

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

    /**
     * Parse the data array.
     *
     * @param array $data
     *
     * @return array
     */
    protected function parseData(array $data): array
    {
        $newData = [];

        foreach ($data as $key => $value) {
            if (\is_array($value)) {
                return $this->parseData($value);
            }
            $newData[$key] = $value;
        }

        return $newData;
    }

    /**
     * Create a rule object.
     *
     * @param array|string $rules
     *
     * @return \Respect\Validation\Validator
     */
    protected function createRule($rules): RespectValidator
    {
        $notRules      = [];
        $optionalRules = [];

        if (\is_string($rules)) {
            // remove duplicate
            $rules = \array_unique(\explode('|', $rules));
        }

        foreach ($rules as $key => $rule) {
            if (\mb_strpos($rule, '!') !== false) {
                $notRules[] = $rule;

                unset($rules[$key]);
            } elseif (\mb_strpos($rule, '?') !== false) {
                $optionalRules[] = $rule;

                unset($rules[$key]);
            }
        }

        // reset keys
        $rules     = \array_values($rules);
        $validator = $this->createValidator($rules, $notRules, $optionalRules);

        return $this->createChainableValidators($validator, $rules);
    }

    /**
     * Create a validator instance.
     *
     * @param array $rules
     * @param array $notRules
     * @param array $optionalRules
     *
     * @throws \RuntimeException
     *
     * @return \Respect\Validation\Validator
     */
    protected function createValidator(array &$rules, array $notRules, array $optionalRules): RespectValidator
    {
        if (\count($notRules) !== 0 && \count($optionalRules) !== 0) {
            throw new InvalidArgumentException('Not (!) and optional (?) cant be used at the same time.');
        }

        if (\count($notRules) !== 0) {
            return $this->createNegativeOrOptionalValidator('!', $notRules);
        }

        if (\count($optionalRules) !== 0) {
            return $this->createNegativeOrOptionalValidator('?', $optionalRules);
        }

        [$method, $parameters] = $this->parseStringRule($rules[0]);

        unset($rules[0]);

        return RespectValidator::$method(...$parameters);
    }

    /**
     * Create a negative or optional validator instance.
     *
     * @param string $filter
     * @param array  $rules
     *
     * @return \Respect\Validation\Validator
     */
    protected function createNegativeOrOptionalValidator(string $filter, array $rules): RespectValidator
    {
        [$method, $parameters] = $this->parseStringRule($rules[0]);

        unset($rules[0]);

        $method    = \str_replace($filter, '', $method);
        $validator = RespectValidator::$method(...$parameters);

        if ($filter === '!') {
            return RespectValidator::not($this->createChainableValidators($validator, $rules));
        }

        return RespectValidator::optional($this->createChainableValidators($validator, $rules));
    }

    /**
     * Chain validator to a chained validator object.
     *
     * @param \Respect\Validation\Validator $class
     * @param array                         $rules
     *
     * @return \Respect\Validation\Validator
     */
    protected function createChainableValidators(RespectValidator $class, array $rules): RespectValidator
    {
        // reset keys
        $rules = \array_values($rules);

        if (\count($rules) !== 0) {
            $chain = '';

            foreach ($rules as $rule) {
                if ($rules[0] === $rule) {
                    $chain .= $rule;
                } else {
                    $chain .= '.' . $rule;
                }
            }

            return \array_reduce(\explode('.', $chain), function (object $validator, string $method) {
                [$method, $parameters] = $this->parseStringRule($method);
                $method                = \str_replace(['!', '?'], '', $method);

                return $validator->{$method}(...$parameters);
            }, $class);
        }

        return $class;
    }

    /**
     * Parse a string based rule.
     *
     * @param string $rules
     *
     * @return array
     */
    protected function parseStringRule(string $rules): array
    {
        $parameters = [];

        // The format for specifying validation rules and parameters follows an
        // easy {rule}:{parameters} formatting convention. For instance the
        // rule "Min:3" states that the value may only be three letters.
        if (\mb_strpos($rules, ':') !== false) {
            [$rules, $parameter] = \explode(':', $rules, 2);

            $parameters = $this->parseParameters($rules, $parameter);
        }

        return [\trim($rules), $parameters];
    }

    /**
     * Parse a parameter list.
     *
     * @param string $rule
     * @param string $parameter
     *
     * @return array
     */
    protected function parseParameters(string $rule, string $parameter): array
    {
        if (\mb_strtolower($rule) === 'regex') {
            return [$parameter];
        }

        return \str_getcsv($parameter);
    }
}


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

use Symfony\Component\Process\PhpExecutableFinder;
use Symfony\Component\Process\Process;
use Viserio\Component\Contract\WebServer\Exception\RuntimeException;

final class WebServer
{
    public const STARTED = 0;

    public const STOPPED = 1;

    /**
     * Private constructor; non-instantiable.
     *
     * @codeCoverageIgnore
     */
    private function __construct()
    {
    }

    /**
     * @param \Viserio\Component\WebServer\WebServerConfig $config
     * @param bool                                         $disableOutput
     * @param null|callable                                $callback
     *
     * @return void
     */
    public static function run(WebServerConfig $config, bool $disableOutput = true, callable $callback = null): void
    {
        if (self::isRunning()) {
            throw new RuntimeException(\sprintf('A process is already listening on http://%s.', $config->getAddress()));
        }

        $process = self::createServerProcess($config);

        if ($disableOutput) {
            $process->disableOutput();
            $callback = null;
        } else {
            try {
                $process->setTty(true);
                $callback = null;
            } catch (\RuntimeException $e) {
            }
        }

        $process->run($callback);

        if (! $process->isSuccessful()) {
            $error = 'Server terminated unexpectedly.';

            if ($process->isOutputDisabled()) {
                $error .= ' Run the command again with -v option for more details.';
            }

            throw new RuntimeException($error);
        }
    }

    /**
     * Starts a local web server in the background.
     *
     * @param \Viserio\Component\WebServer\WebServerConfig $config
     * @param null|string                                  $pidFile
     *
     * @return int
     */
    public static function start(WebServerConfig $config, string $pidFile = null): int
    {
        $pidFile = $pidFile ?? self::getDefaultPidFile();

        if (self::isRunning($pidFile)) {
            throw new RuntimeException(\sprintf('A process is already listening on http://%s.', $config->getAddress()));
        }

        $pid = pcntl_fork();

        if ($pid < 0) {
            throw new RuntimeException('Unable to start the server process.');
        }

        if ($pid > 0) {
            return self::STARTED;
        }

        if (posix_setsid() < 0) {
            throw new RuntimeException('Unable to set the child process as session leader.');
        }

        $process = self::createServerProcess($config);
        $process->disableOutput();
        $process->start();

        if (! $process->isRunning()) {
            throw new RuntimeException('Unable to start the server process.');
        }

        \file_put_contents($pidFile, $config->getAddress());

        // stop the web server when the lock file is removed
        while ($process->isRunning()) {
            if (! \file_exists($pidFile)) {
                $process->stop();
            }

            \sleep(1);
        }

        return self::STOPPED;
    }

    /**
     * Stop a running web server.
     *
     * @param null|string $pidFile
     *
     * @return void
     */
    public static function stop(string $pidFile = null): void
    {
        $pidFile = $pidFile ?? self::getDefaultPidFile();

        if (! \file_exists($pidFile)) {
            throw new RuntimeException('No web server is listening.');
        }

        \unlink($pidFile);
    }

    /**
     * Get the address from the pid file.
     *
     * @param null|string $pidFile
     *
     * @return bool|string
     */
    public static function getAddress(string $pidFile = null)
    {
        $pidFile = $pidFile ?? self::getDefaultPidFile();

        if (! \file_exists($pidFile)) {
            return false;
        }

        return \file_get_contents($pidFile);
    }

    /**
     * Check if a server is running.
     *
     * @param null|string $pidFile
     *
     * @return bool
     */
    public static function isRunning(string $pidFile = null): bool
    {
        $pidFile = $pidFile ?? self::getDefaultPidFile();

        if (! \file_exists($pidFile)) {
            return false;
        }

        $address  = \file_get_contents($pidFile);
        $pos      = \mb_strrpos($address, ':');
        $hostname = \mb_substr($address, 0, $pos);
        $port     = \mb_substr($address, $pos + 1);

        if (false !== $fp = @fsockopen($hostname, (int) $port, $errno, $errstr, 1)) {
            fclose($fp);

            return true;
        }

        \unlink($pidFile);

        return false;
    }

    /**
     * Create a new server command process.
     *
     * @param \Viserio\Component\WebServer\WebServerConfig $config
     *
     * @return \Symfony\Component\Process\Process
     */
    private static function createServerProcess(WebServerConfig $config): Process
    {
        $finder = new PhpExecutableFinder();

        if (($binary = $finder->find(false)) === false) {
            throw new RuntimeException('Unable to find the PHP binary.');
        }

        $xdebugArgs = [];

        if ($config->hasXdebug() && \extension_loaded('xdebug')) {
            $xdebugArgs = ['-dxdebug.profiler_enable_trigger=1'];
        }

        $process = new Process(\array_merge([$binary], $finder->findArguments(), $xdebugArgs, ['-dvariables_order=EGPCS', '-S', $config->getAddress(), $config->getRouter()]));
        $process->setWorkingDirectory($config->getDocumentRoot());
        $process->setTimeout(null);

        return $process;
    }

    /**
     * Get the default pid file path.
     *
     * @return string
     */
    private static function getDefaultPidFile(): string
    {
        return \getcwd() . \DIRECTORY_SEPARATOR . '.web-server-pid';
    }
}


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

use Closure;
use Viserio\Component\Contract\Events\Event as EventContract;
use Viserio\Component\Contract\Events\EventManager as EventManagerContract;
use Viserio\Component\Events\Traits\ValidateNameTrait;

class EventManager implements EventManagerContract
{
    use ValidateNameTrait;

    /**
     * The registered event listeners.
     *
     * @var array
     */
    protected $listeners = [];

    /**
     * The sorted event listeners.
     *
     * @var array
     */
    private $sorted = [];

    /**
     * The synced events.
     *
     * @var array
     */
    private $syncedEvents = [];

    /**
     * Wildcard patterns.
     *
     * @var array
     */
    private $patterns = [];

    /**
     * Returns the list of listeners for an event.
     *
     * The list is returned as an array, and the list of events are sorted by
     * their priority.
     *
     * @param null|string $eventName
     *
     * @return array
     *
     * @internal
     */
    public function getListeners(string $eventName = null): array
    {
        if ($eventName === null) {
            foreach ($this->listeners as $name => $eventListeners) {
                if (! isset($this->sorted[$name])) {
                    $this->sortListeners($name);
                }
            }

            return \array_filter($this->sorted);
        }

        $this->validateEventName($eventName);

        $this->bindPatterns($eventName);

        if (! isset($this->listeners[$eventName])) {
            return [];
        }

        if (! isset($this->sorted[$eventName])) {
            $this->sortListeners($eventName);
        }

        return $this->sorted[$eventName];
    }

    /**
     * Gets the listener priority for a specific event.
     *
     * Returns null if the event or the listener does not exist.
     *
     * @param string         $eventName The name of the event
     * @param array|callable $listener  The listener
     *
     * @return null|int The event listener priority
     *
     * @internal
     */
    public function getListenerPriority(string $eventName, $listener): ?int
    {
        if (! isset($this->listeners[$eventName]) && \count($this->listeners[$eventName]) === 0) {
            return null;
        }

        if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof Closure) {
            $listener[0] = $listener[0]();
        }

        foreach ($this->listeners[$eventName] as $priority => $listeners) {
            foreach ($listeners as $key => $value) {
                if ($value !== $listener && \is_array($value) && isset($value[0]) && $value[0] instanceof Closure) {
                    $value[0]                                     = $value[0]();
                    $this->listeners[$eventName][$priority][$key] = $value;
                }

                if ($listener === $value) {
                    return $priority;
                }
            }
        }

        return null;
    }

    /**
     * {@inheritdoc}
     */
    public function attach(string $eventName, $listener, int $priority = 0): void
    {
        $this->validateEventName($eventName);

        if ($this->hasWildcards($eventName)) {
            $this->addListenerPattern(new ListenerPattern($eventName, $listener, $priority));
        } else {
            $this->listeners[$eventName][$priority][] = $listener;

            unset($this->sorted[$eventName]);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function trigger($event, $target = null, array $argv = []): bool
    {
        if ((! \is_object($event) && ! ($event instanceof EventContract)) && \is_string($event)) {
            $event = new Event($event, $target, $argv);
        }

        if ($event->isPropagationStopped()) {
            return false;
        }

        $listeners = $this->getListeners($event->getName());

        foreach ($listeners as $listener) {
            $result = false;

            if ($listener !== null) {
                $result = $listener($event);
            }

            if ($result === false) {
                return false;
            }
        }

        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function detach(string $eventName, $listener): bool
    {
        $this->validateEventName($eventName);

        if ($this->hasWildcards($eventName)) {
            $this->removeListenerPattern($eventName, $listener);

            return true;
        }

        if (\count($this->getListeners($eventName)) === 0) {
            return false;
        }

        if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) {
            $listener[0] = $listener[0]();
        }

        $bool = false;

        foreach ($this->listeners[$eventName] as $priority => $listeners) {
            foreach ($listeners as $key => $value) {
                if ($listener !== $value && \is_array($value) && isset($value[0]) && $value[0] instanceof Closure) {
                    $value[0] = $value[0]();
                }

                if ($value === $listener) {
                    unset($listeners[$key], $this->sorted[$eventName]);

                    $bool = true;
                } else {
                    $listeners[$key] = $value;
                }
            }

            if ($listeners) {
                $this->listeners[$eventName][$priority] = $listeners;
            } else {
                unset($this->listeners[$eventName][$priority]);

                $bool = true;
            }
        }

        return $bool;
    }

    /**
     * {@inheritdoc}
     */
    public function clearListeners(string $eventName): void
    {
        $this->validateEventName($eventName);

        unset($this->listeners[$eventName], $this->syncedEvents[$eventName]);
    }

    /**
     * Sort the listeners for a given event by priority.
     *
     * @param string $eventName
     *
     * @return void
     */
    protected function sortListeners(string $eventName): void
    {
        $this->sorted[$eventName] = [];

        // If listeners exist for the given event, we will sort them by the priority
        // so that we can call them in the correct order. We will cache off these
        // sorted event listeners so we do not have to re-sort on every events.
        if (isset($this->listeners[$eventName])) {
            \krsort($this->listeners[$eventName]);

            foreach ($this->listeners[$eventName] as $priority => $listeners) {
                foreach ($listeners as $k => $listener) {
                    if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof Closure) {
                        $listener[0]                                = $listener[0]();
                        $this->listeners[$eventName][$priority][$k] = $listener;
                    }

                    $this->sorted[$eventName][] = $listener;
                }
            }
        }
    }

    /**
     * Binds all patterns that match the specified event name.
     *
     * @param string $eventName
     *
     * @return void
     */
    protected function bindPatterns(string $eventName): void
    {
        if (isset($this->syncedEvents[$eventName])) {
            return;
        }

        foreach ($this->patterns as $eventPattern => $patterns) {
            /** @var ListenerPattern $pattern */
            foreach ($patterns as $pattern) {
                if ($pattern->test($eventName)) {
                    $pattern->bind($this, $eventName);
                }
            }
        }

        $this->syncedEvents[$eventName] = true;
    }

    /**
     * Removes an event listener from any events to which it was applied due to
     * pattern matching.
     *
     * This method cannot be used to remove a listener from a pattern that was
     * never registered.
     *
     * @param string $eventPattern
     * @param mixed  $listener
     *
     * @return void
     */
    protected function removeListenerPattern(string $eventPattern, $listener): void
    {
        if (empty($this->patterns[$eventPattern])) {
            return;
        }

        /** @var ListenerPattern $pattern */
        foreach ($this->patterns[$eventPattern] as $key => $pattern) {
            if ($listener === $pattern->getListener()) {
                $pattern->unbind($this);

                unset($this->patterns[$eventPattern][$key]);
            }
        }
    }

    /**
     * Checks whether a string contains any wildcard characters.
     *
     * @param string $subject
     *
     * @return bool
     */
    private function hasWildcards(string $subject): bool
    {
        return \mb_strpos($subject, '*') !== false || \mb_strpos($subject, '#') !== false;
    }

    /**
     * Adds an event listener for all events matching the specified pattern.
     *
     * This method will lazily register the listener when a matching event is
     * dispatched.
     *
     * @param \Viserio\Component\Events\ListenerPattern $pattern
     *
     * @return void
     */
    private function addListenerPattern(ListenerPattern $pattern): void
    {
        $this->patterns[$pattern->getEventPattern()][] = $pattern;

        foreach ($this->syncedEvents as $eventName => $value) {
            if ($pattern->test($eventName)) {
                unset($this->syncedEvents[$eventName]);
            }
        }
    }
}


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

use Interop\Container\ServiceProviderInterface;
use Psr\Container\ContainerInterface;
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\Contract\Routing\Router as RouterContract;
use Viserio\Component\OptionsResolver\Traits\OptionsResolverTrait;
use Viserio\Component\Routing\DataCollector\RoutingDataCollector;

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

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

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

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

    /**
     * {@inheritdoc}
     */
    public static function getDefaultOptions(): array
    {
        return [
            'collector' => [
                'routes' => 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']['routes']) {
                $profiler->addCollector(
                    new RoutingDataCollector(
                        $container->get(RouterContract::class)->getRoutes()
                    )
                );
            }
        }

        return $profiler;
    }
}


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

use ReflectionClass;
use Viserio\Component\Contract\OptionsResolver\ProvidesDefaultOptions as ProvidesDefaultOptionsContract;
use Viserio\Component\Contract\OptionsResolver\RequiresComponentConfig as RequiresComponentConfigContract;
use Viserio\Component\Contract\OptionsResolver\RequiresConfig as RequiresConfigContract;
use Viserio\Component\Contract\OptionsResolver\RequiresMandatoryOptions as RequiresMandatoryOptionsContract;

class OptionsReader
{
    /**
     * @param \ReflectionClass $reflectionClass
     *
     * @return array
     */
    public function readConfig(ReflectionClass $reflectionClass): array
    {
        $interfaces = \array_flip($reflectionClass->getInterfaceNames());

        if (isset($interfaces[RequiresConfigContract::class])) {
            $dimensions       = [];
            $defaultOptions   = [];
            $key              = null;

            if (isset($interfaces[RequiresComponentConfigContract::class])) {
                $dimensions = $reflectionClass->getName()::getDimensions();
                $key        = \end($dimensions);
            }

            if (isset($interfaces[ProvidesDefaultOptionsContract::class])) {
                $defaultOptions = $reflectionClass->getName()::getDefaultOptions();
            }

            if (isset($interfaces[RequiresMandatoryOptionsContract::class])) {
                $config = \array_merge_recursive(
                    $defaultOptions,
                    $this->readMandatoryOption($reflectionClass->getName(), $dimensions, $reflectionClass->getName()::getMandatoryOptions())
                );
            } else {
                $config = $defaultOptions;
            }

            if (\count($dimensions) !== 0) {
                $config = $this->buildMultidimensionalArray($dimensions, $config);
            }

            if ($key !== null) {
                return [$key => $config];
            }

            return $config;
        }

        return [];
    }

    /**
     * Read the mandatory options.
     *
     * @param string $className
     * @param array  $dimensions
     * @param array  $mandatoryOptions
     *
     * @return array
     */
    protected function readMandatoryOption(string $className, array $dimensions, array $mandatoryOptions): array
    {
        $options = [];

        foreach ($mandatoryOptions as $key => $mandatoryOption) {
            if (! \is_scalar($mandatoryOption)) {
                $options[$key] = $this->readMandatoryOption($className, $dimensions, $mandatoryOptions[$key]);

                continue;
            }

            $options[$mandatoryOption] = null;
        }

        return $options;
    }

    /**
     * Builds a multidimensional config array.
     *
     * @param array $dimensions
     * @param mixed $value
     *
     * @return array
     */
    private function buildMultidimensionalArray(array $dimensions, $value): array
    {
        $config = [];
        $index  = \array_shift($dimensions);

        if (! isset($dimensions[0])) {
            $config[$index] = $value;
        } else {
            $config[$index] = $this->buildMultidimensionalArray($dimensions, $value);
        }

        return $config;
    }
}


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

use Narrowspark\Http\Message\Util\Traits\AcceptHeaderTrait;
use Narrowspark\HttpStatus\HttpStatus;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Debug\Exception\FlattenException;
use Throwable;
use Viserio\Component\Contract\Exception\Displayer as DisplayerContract;
use Viserio\Component\Contract\Exception\Filter as FilterContract;
use Viserio\Component\Contract\Exception\HttpHandler as HttpHandlerContract;
use Viserio\Component\Contract\HttpFactory\Traits\ResponseFactoryAwareTrait;
use Viserio\Component\Contract\OptionsResolver\RequiresMandatoryOptions as RequiresMandatoryOptionsContract;
use Viserio\Component\Exception\Displayer\HtmlDisplayer;
use Viserio\Component\Exception\ErrorHandler;
use Viserio\Component\Exception\ExceptionIdentifier;
use Viserio\Component\Exception\Traits\RegisterAndUnregisterTrait;

class Handler extends ErrorHandler implements HttpHandlerContract, RequiresMandatoryOptionsContract
{
    use ResponseFactoryAwareTrait;
    use AcceptHeaderTrait;
    use RegisterAndUnregisterTrait;

    /**
     * Exception filters.
     *
     * @var array
     */
    private $filters = [];

    /**
     * Exception displayers.
     *
     * @var array
     */
    private $displayers = [];

    /**
     * Create a new handler instance.
     *
     * @param array|\ArrayAccess                         $config
     * @param \Psr\Http\Message\ResponseFactoryInterface $responseFactory
     * @param null|\Psr\Log\LoggerInterface              $logger
     */
    public function __construct($config, ResponseFactoryInterface $responseFactory, ?LoggerInterface $logger = null)
    {
        parent::__construct($config, $logger);

        $this->setResponseFactory($responseFactory);
    }

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

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

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

    /**
     * {@inheritdoc}
     */
    public static function getDefaultOptions(): array
    {
        return \array_merge(
            parent::getDefaultOptions(),
            [
                'http' => [
                    'displayer' => [
                        'default' => HtmlDisplayer::class,
                    ],
                ],
            ]
        );
    }

    /**
     * {@inheritdoc}
     */
    public function addDisplayer(DisplayerContract $displayer, int $priority = 0): HttpHandlerContract
    {
        $this->displayers[$priority][\get_class($displayer)] = $displayer;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function addFilter(FilterContract $filter, int $priority = 0): HttpHandlerContract
    {
        $this->filters[$priority][\get_class($filter)] = $filter;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function handleException(Throwable $exception): void
    {
        try {
            parent::handleException($exception);
        } catch (Throwable $transformedException) {
            $response = $this->getPreparedResponse(
                null,
                $exception,
                $transformedException
            );

            echo (string) $response->getBody();
        }
    }

    /**
     * {@inheritdoc}
     */
    public function render(ServerRequestInterface $request, Throwable $exception): ResponseInterface
    {
        $exception = $this->prepareException($exception);

        return $this->getPreparedResponse(
            $request,
            $exception,
            $this->getTransformed($exception)
        );
    }

    /**
     * Get a prepared response with the transformed exception.
     *
     * @param null|\Psr\Http\Message\ServerRequestInterface $request
     * @param \Throwable                                    $exception
     * @param \Throwable                                    $transformed
     *
     * @return \Psr\Http\Message\ResponseInterface
     */
    protected function getPreparedResponse(
        ?ServerRequestInterface $request,
        Throwable $exception,
        Throwable $transformed
    ): ResponseInterface {
        try {
            $response = $this->getResponse(
                $request,
                $exception,
                $transformed
            );
        } catch (Throwable $exception) {
            $this->report($exception);

            $response = $this->responseFactory->createResponse();
            $response = $response->withStatus(500, HttpStatus::getReasonPhrase(500));
            $response = $response->withHeader('content-type', 'text/plain');
        }

        return $response;
    }

    /**
     * Create a response for the given exception.
     *
     * @param null|\Psr\Http\Message\ServerRequestInterface $request
     * @param \Throwable                                    $exception
     * @param \Throwable                                    $transformed
     *
     * @return \Psr\Http\Message\ResponseInterface
     */
    protected function getResponse(
        ?ServerRequestInterface $request,
        Throwable $exception,
        Throwable $transformed
    ): ResponseInterface {
        $id        = ExceptionIdentifier::identify($exception);
        $flattened = FlattenException::create($exception);
        $code      = $flattened->getStatusCode();
        $headers   = $flattened->getHeaders();

        return $this->getDisplayer(
            $request,
            $exception,
            $transformed,
            $code
        )->display($transformed, $id, $code, $headers);
    }

    /**
     * Get the displayer instance.
     *
     * @param null|\Psr\Http\Message\ServerRequestInterface $request
     * @param \Throwable                                    $original
     * @param \Throwable                                    $transformed
     * @param int                                           $code
     *
     * @return \Viserio\Component\Contract\Exception\Displayer
     */
    protected function getDisplayer(
        ?ServerRequestInterface $request,
        Throwable $original,
        Throwable $transformed,
        int $code
    ): DisplayerContract {
        $sortedDisplayers = [];

        \ksort($this->displayers);

        \array_walk_recursive($this->displayers, static function ($displayers, $key) use (&$sortedDisplayers): void {
            $sortedDisplayers[$key] = $displayers;
        });

        if ($request !== null) {
            $filtered = $this->getFiltered($sortedDisplayers, $request, $original, $transformed, $code);

            if (\count($filtered) !== 0) {
                return $this->sortedFilter($filtered, $request);
            }
        }

        return $sortedDisplayers[$this->resolvedOptions['http']['displayer']['default']];
    }

    /**
     * Get the filtered list of displayers.
     *
     * @param \Viserio\Component\Contract\Exception\Displayer[] $displayers
     * @param \Psr\Http\Message\ServerRequestInterface          $request
     * @param \Throwable                                        $original
     * @param \Throwable                                        $transformed
     * @param int                                               $code
     *
     * @return \Viserio\Component\Contract\Exception\Displayer[]
     */
    protected function getFiltered(
        array $displayers,
        ServerRequestInterface $request,
        Throwable $original,
        Throwable $transformed,
        int $code
    ): array {
        /** @var \Viserio\Component\Contract\Exception\Filter[] $sortedFilters */
        $sortedFilters = [];

        \ksort($this->filters);

        \array_walk_recursive($this->filters, static function ($filter, $key) use (&$sortedFilters): void {
            $sortedFilters[$key] = $filter;
        });

        foreach ($sortedFilters as $filter) {
            $displayers = $filter->filter($displayers, $request, $original, $transformed, $code);
        }

        return \array_values($displayers);
    }

    /**
     * Sort displayer after the first found accept header.
     *
     * @param array                                    $filtered
     * @param \Psr\Http\Message\ServerRequestInterface $request
     *
     * @return \Viserio\Component\Contract\Exception\Displayer
     */
    private function sortedFilter(array $filtered, ServerRequestInterface $request): DisplayerContract
    {
        $accepts = self::getHeaderValuesFromString($request->getHeaderLine('Accept'));

        foreach ($accepts as $accept) {
            foreach ($filtered as $filter) {
                if ($filter->getContentType() === $accept) {
                    return $filter;
                }
            }
        }

        return $filtered[0];
    }
}


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

use Viserio\Component\Contract\Routing\Exception\RuntimeException;
use Viserio\Component\Contract\Routing\Route as RouteContract;
use Viserio\Component\Contract\Routing\RouteCollection as RouteCollectionContract;

class Collection implements RouteCollectionContract
{
    /**
     * An flattened array of all of the routes.
     *
     * @var array
     */
    protected $allRoutes = [];

    /**
     * A look-up table of routes by their names.
     *
     * @var array
     */
    protected $nameList = [];

    /**
     * A look-up table of routes by controller action.
     *
     * @var array
     */
    protected $actionList = [];

    /**
     * {@inheritdoc}
     */
    public function add(RouteContract $route): RouteContract
    {
        $domainAndUri = $route->getDomain() . $route->getUri();

        $this->allRoutes[\implode('|', $route->getMethods()) . $domainAndUri] = $route;

        $this->addLookups($route);

        return $route;
    }

    /**
     * {@inheritdoc}
     */
    public function match(string $identifier): RouteContract
    {
        if (isset($this->allRoutes[$identifier])) {
            return $this->allRoutes[$identifier];
        }

        throw new RuntimeException('Route not found, looks like your route cache is stale.');
    }

    /**
     * {@inheritdoc}
     */
    public function hasNamedRoute(string $name): bool
    {
        return null !== $this->getByName($name);
    }

    /**
     * {@inheritdoc}
     */
    public function getByName(string $name): ?RouteContract
    {
        return $this->nameList[$name] ?? null;
    }

    /**
     * {@inheritdoc}
     */
    public function getByAction(string $action): ?RouteContract
    {
        return $this->actionList[$action] ?? null;
    }

    /**
     * {@inheritdoc}
     */
    public function getRoutes(): array
    {
        return \array_values($this->allRoutes);
    }

    /**
     * Count the number of items in the collection.
     *
     * @return int
     */
    public function count(): int
    {
        return \count($this->allRoutes);
    }

    /**
     * Add the route to any look-up tables if necessary.
     *
     * @param \Viserio\Component\Contract\Routing\Route $route
     *
     * @return void
     */
    protected function addLookups(RouteContract $route): void
    {
        // If the route has a name, we will add it to the name look-up table so that we
        // will quickly be able to find any route associate with a name and not have
        // to iterate through every route every time we need to perform a look-up.
        $action = $route->getAction();

        if (isset($action['as'])) {
            $this->nameList[$action['as']] = $route;
        }

        // When the route is routing to a controller we will also store the action that
        // is used by the route. This will let us reverse route to controllers while
        // processing a request and easily generate URLs to the given controllers.
        if (isset($action['controller'])) {
            $this->actionList[\trim($action['controller'], '\\')] = $route;
        }
    }
}


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

use Swift_Attachment;
use Swift_Image;
use Swift_Mime_Attachment;
use Swift_Mime_SimpleMessage;
use Viserio\Component\Contract\Mail\Message as MessageContract;

/**
 * @mixin \Swift_Mime_SimpleMessage
 */
class Message implements MessageContract
{
    /**
     * The Swift Message instance.
     *
     * @var \Swift_Mime_SimpleMessage
     */
    protected $swift;

    /**
     * Create a new message instance.
     *
     * @param \Swift_Mime_SimpleMessage $swift
     */
    public function __construct(Swift_Mime_SimpleMessage $swift)
    {
        $this->swift = $swift;
    }

    /**
     * Dynamically pass missing methods to the Swift instance.
     *
     * @param string $method
     * @param array  $parameters
     *
     * @return mixed
     */
    public function __call(string $method, array $parameters)
    {
        return $this->swift->{$method}(...$parameters);
    }

    /**
     * {@inheritdoc}
     */
    public function from(string $address, string $name = null): MessageContract
    {
        $this->swift->setFrom($address, $name);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function sender(string $address, string $name = null): MessageContract
    {
        $this->swift->setSender($address, $name);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function returnPath(string $address): MessageContract
    {
        $this->swift->setReturnPath($address);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function to($address, string $name = null, bool $override = false): MessageContract
    {
        if ($override) {
            $this->swift->setTo($address, $name);

            return $this;
        }

        $this->addAddresses($address, $name, 'To');

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function cc($address, string $name = null, bool $override = false): MessageContract
    {
        if ($override) {
            $this->swift->setCc($address, $name);

            return $this;
        }

        $this->addAddresses($address, $name, 'Cc');

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function bcc($address, string $name = null, bool $override = false): MessageContract
    {
        if ($override) {
            $this->swift->setBcc($address, $name);

            return $this;
        }

        $this->addAddresses($address, $name, 'Bcc');

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function replyTo(string $address, string $name = null): MessageContract
    {
        $this->addAddresses($address, $name, 'ReplyTo');

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function subject(string $subject): MessageContract
    {
        $this->swift->setSubject($subject);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function priority(int $level): MessageContract
    {
        $this->swift->setPriority($level);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function attach(string $file, array $options = []): MessageContract
    {
        $attachment = $this->createAttachmentFromPath($file);

        $this->prepAttachment($attachment, $options);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function attachData(string $data, string $name, array $options = []): MessageContract
    {
        $attachment = $this->createAttachmentFromData($data, $name);

        $this->prepAttachment($attachment, $options);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function embed(string $file): string
    {
        return $this->swift->embed(Swift_Image::fromPath($file));
    }

    /**
     * {@inheritdoc}
     */
    public function embedData(string $data, string $name, string $contentType = null): string
    {
        $image = new Swift_Image($data, $name, $contentType);

        return $this->swift->embed($image);
    }

    /**
     * Get the underlying Swift Message instance.
     *
     * @return \Swift_Mime_SimpleMessage
     */
    public function getSwiftMessage(): Swift_Mime_SimpleMessage
    {
        return $this->swift;
    }

    /**
     * Add a recipient to the message.
     *
     * @param array|string $address
     * @param string       $name
     * @param string       $type
     *
     * @return void
     */
    protected function addAddresses($address, string $name, string $type): void
    {
        if (\is_array($address)) {
            $set = \sprintf('set%s', $type);
            $this->swift->{$set}($address, $name);

            return;
        }

        $add = \sprintf('add%s', $type);
        $this->swift->{$add}($address, $name);
    }

    /**
     * Create a Swift Attachment instance.
     *
     * @param string $file
     *
     * @return \Swift_Mime_Attachment
     */
    protected function createAttachmentFromPath(string $file): Swift_Mime_Attachment
    {
        return Swift_Attachment::fromPath($file);
    }

    /**
     * Create a Swift Attachment instance from data.
     *
     * @param string $data
     * @param string $name
     *
     * @return \Swift_Mime_Attachment
     */
    protected function createAttachmentFromData(string $data, string $name): Swift_Mime_Attachment
    {
        return new Swift_Attachment($data, $name);
    }

    /**
     * Prepare and attach the given attachment.
     *
     * @param \Swift_Mime_Attachment $attachment
     * @param array                  $options
     *
     * @return void
     */
    protected function prepAttachment(Swift_Mime_Attachment $attachment, array $options = []): void
    {
        // First we will check for a MIME type on the message, which instructs the
        // mail client on what type of attachment the file is so that it may be
        // downloaded correctly by the user. The MIME option is not required.
        if (isset($options['mime'])) {
            $attachment->setContentType($options['mime']);
        }

        // If an alternative name was given as an option, we will set that on this
        // attachment so that it will be downloaded with the desired names from
        // the developer, otherwise the default file names will get assigned.
        if (isset($options['as'])) {
            $attachment->setFilename($options['as']);
        }

        $this->swift->attach($attachment);
    }
}


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

use Interop\Container\ServiceProviderInterface;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Log\LoggerInterface as PsrLoggerInterface;
use Symfony\Component\Stopwatch\Stopwatch;
use Viserio\Component\Contract\Events\EventManager as EventManagerContract;
use Viserio\Component\Contract\Foundation\Terminable as TerminableContract;
use Viserio\Component\Contract\OptionsResolver\ProvidesDefaultOptions as ProvidesDefaultOptionsContract;
use Viserio\Component\Contract\OptionsResolver\RequiresComponentConfig as RequiresComponentConfigContract;
use Viserio\Component\Contract\OptionsResolver\RequiresMandatoryOptions as RequiresMandatoryOptionsContract;
use Viserio\Component\Contract\Profiler\Profiler as ProfilerContract;
use Viserio\Component\Contract\Routing\Router as RouterContract;
use Viserio\Component\Contract\Routing\UrlGenerator as UrlGeneratorContract;
use Viserio\Component\OptionsResolver\Traits\OptionsResolverTrait;
use Viserio\Component\Profiler\AssetsRenderer;
use Viserio\Component\Profiler\DataCollector\AjaxRequestsDataCollector;
use Viserio\Component\Profiler\DataCollector\MemoryDataCollector;
use Viserio\Component\Profiler\DataCollector\PhpInfoDataCollector;
use Viserio\Component\Profiler\DataCollector\TimeDataCollector;
use Viserio\Component\Profiler\Profiler;

class ProfilerServiceProvider implements
    ServiceProviderInterface,
    RequiresComponentConfigContract,
    ProvidesDefaultOptionsContract,
    RequiresMandatoryOptionsContract
{
    use OptionsResolverTrait;

    /**
     * {@inheritdoc}
     */
    public function getFactories(): array
    {
        return [
            AssetsRenderer::class   => [self::class, 'createAssetsRenderer'],
            ProfilerContract::class => [self::class, 'createProfiler'],
            Profiler::class         => static function (ContainerInterface $container) {
                return $container->get(ProfilerContract::class);
            },
            Stopwatch::class => static function () {
                return new Stopwatch();
            },
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function getExtensions(): array
    {
        return [
            RouterContract::class       => [self::class, 'extendRouter'],
            EventManagerContract::class => [self::class, 'extendEventManager'],
        ];
    }

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

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

    /**
     * {@inheritdoc}
     */
    public static function getDefaultOptions(): array
    {
        return [
            'collector' => [
                'time'    => true,
                'memory'  => true,
                'ajax'    => false,
                'phpinfo' => false,
            ],
            'jquery_is_used' => false,
            'path'           => null,
            'collectors'     => [],
        ];
    }

    /**
     * Register profiler asset controllers.
     *
     * @param \Psr\Container\ContainerInterface                    $container
     * @param null|\Viserio\Component\Contract\Events\EventManager $eventManager
     *
     * @return null|\Viserio\Component\Contract\Events\EventManager
     */
    public static function extendEventManager(
        ContainerInterface $container,
        ?EventManagerContract $eventManager = null
    ): ?EventManagerContract {
        if ($eventManager !== null) {
            $eventManager->attach(TerminableContract::TERMINATE, static function () use ($container): void {
                $container->get(ProfilerContract::class)->reset();
            });
        }

        return $eventManager;
    }

    public static function createProfiler(ContainerInterface $container): ProfilerContract
    {
        $options  = self::resolveOptions($container->get('config'));
        $profiler = new Profiler($container->get(AssetsRenderer::class));

        if ($options['enable']) {
            $profiler->enable();
        }

        if ($container->has(CacheItemPoolInterface::class)) {
            $profiler->setCacheItemPool($container->get(CacheItemPoolInterface::class));
        }

        if ($container->has(PsrLoggerInterface::class)) {
            $profiler->setLogger($container->get(PsrLoggerInterface::class));
        }

        $profiler->setStreamFactory(
            $container->get(StreamFactoryInterface::class)
        );

        if ($container->has(UrlGeneratorContract::class)) {
            $profiler->setUrlGenerator($container->get(UrlGeneratorContract::class));
        }

        self::registerCollectorsFromConfig($container, $profiler, $options);
        self::registerBaseCollectors($container, $profiler, $options);

        return $profiler;
    }

    /**
     * Create a new AssetsRenderer instance.
     *
     * @param \Psr\Container\ContainerInterface $container
     *
     * @return \Viserio\Component\Profiler\AssetsRenderer
     */
    public static function createAssetsRenderer(ContainerInterface $container): AssetsRenderer
    {
        $options = self::resolveOptions($container->get('config'));

        return new AssetsRenderer($options['jquery_is_used'], $options['path']);
    }

    /**
     * Register profiler asset controllers.
     *
     * @param \Psr\Container\ContainerInterface               $container
     * @param null|\Viserio\Component\Contract\Routing\Router $router
     *
     * @return null|\Viserio\Component\Contract\Routing\Router
     */
    public static function extendRouter(ContainerInterface $container, ?RouterContract $router = null): RouterContract
    {
        if ($router !== null) {
            $router->group(
                [
                    'namespace' => 'Viserio\Component\Profiler\Controller',
                    'prefix'    => 'profiler',
                ],
                static function ($router): void {
                    $router->get('assets/stylesheets', [
                        'uses' => 'AssetController@css',
                        'as'   => 'profiler.assets.css',
                    ]);
                    $router->get('assets/javascript', [
                        'uses' => 'AssetController@js',
                        'as'   => 'profiler.assets.js',
                    ]);
                }
            );
        }

        return $router;
    }

    /**
     * Register base collectors.
     *
     * @param \Psr\Container\ContainerInterface             $container
     * @param \Viserio\Component\Contract\Profiler\Profiler $profiler
     * @param array                                         $options
     *
     * @return void
     */
    protected static function registerBaseCollectors(
        ContainerInterface $container,
        ProfilerContract $profiler,
        array $options
    ): void {
        if ($options['collector']['time']) {
            $profiler->addCollector(new TimeDataCollector(
                $container->get(ServerRequestInterface::class)
            ));
        }

        if ($options['collector']['memory']) {
            $profiler->addCollector(new MemoryDataCollector());
        }

        if ($options['collector']['ajax']) {
            $profiler->addCollector(new AjaxRequestsDataCollector());
        }

        if ($options['collector']['phpinfo']) {
            $profiler->addCollector(new PhpInfoDataCollector());
        }
    }

    /**
     * Register all found collectors in config.
     *
     * @param \Psr\Container\ContainerInterface             $container
     * @param \Viserio\Component\Contract\Profiler\Profiler $profiler
     * @param array                                         $options
     *
     * @return void
     */
    private static function registerCollectorsFromConfig(
        ContainerInterface $container,
        ProfilerContract $profiler,
        array $options
    ): void {
        if ($collectors = $options['collectors']) {
            foreach ($collectors as $collector) {
                $profiler->addCollector($container->get($collector));
            }
        }
    }
}


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

use Interop\Container\ServiceProviderInterface;
use Psr\Container\ContainerInterface;
use Viserio\Component\Contract\Cache\Manager as CacheManagerContract;
use Viserio\Component\Contract\Cookie\QueueingFactory as JarContract;
use Viserio\Component\Contract\Events\Event as EventContract;
use Viserio\Component\Contract\Events\EventManager as EventManagerContract;
use Viserio\Component\Contract\Foundation\Terminable as TerminableContract;
use Viserio\Component\Contract\Session\Store as StoreContract;
use Viserio\Component\Session\Handler\CookieSessionHandler;
use Viserio\Component\Session\SessionManager;

class SessionServiceProvider implements ServiceProviderInterface
{
    /**
     * {@inheritdoc}
     */
    public function getFactories(): array
    {
        return [
            SessionManager::class => [self::class, 'createSessionManager'],
            'session'             => static function (ContainerInterface $container) {
                return $container->get(SessionManager::class);
            },
            'session.store' => [self::class, 'createSessionStore'],
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function getExtensions(): array
    {
        return [
            EventManagerContract::class => [self::class, 'extendEventManager'],
        ];
    }

    /**
     * Extend viserio events with data collector.
     *
     * @param \Psr\Container\ContainerInterface                    $container
     * @param null|\Viserio\Component\Contract\Events\EventManager $eventManager
     *
     * @return null|\Viserio\Component\Contract\Events\EventManager
     */
    public static function extendEventManager(
        ContainerInterface $container,
        ?EventManagerContract $eventManager = null
    ): ?EventManagerContract {
        if ($eventManager !== null) {
            $eventManager->attach(TerminableContract::TERMINATE, static function (EventContract $event): void {
                /** @var StoreContract $driver */
                $driver = $event->getTarget()->getContainer()->get(SessionManager::class)->getDriver();

                if (! $driver->getHandler() instanceof CookieSessionHandler) {
                    $driver->save();
                }
            });
        }

        return $eventManager;
    }

    /**
     * Create new session manager instance.
     *
     * @param \Psr\Container\ContainerInterface $container
     *
     * @return \Viserio\Component\Session\SessionManager
     */
    public static function createSessionManager(ContainerInterface $container): SessionManager
    {
        $manager = new SessionManager($container->get('config'));

        if ($container->has(CacheManagerContract::class)) {
            $manager->setCacheManager($container->get(CacheManagerContract::class));
        }

        if ($container->has(JarContract::class)) {
            $manager->setCookieJar($container->get(JarContract::class));
        }

        return $manager;
    }

    /**
     * Create session store from default driver.
     *
     * @param \Psr\Container\ContainerInterface $container
     *
     * @return \Viserio\Component\Contract\Session\Store
     */
    public static function createSessionStore(ContainerInterface $container): StoreContract
    {
        return $container->get(SessionManager::class)->getDriver();
    }
}


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

use Interop\Container\ServiceProviderInterface;
use Psr\Container\ContainerInterface;
use Viserio\Component\Console\Application;
use Viserio\Component\Contract\OptionsResolver\ProvidesDefaultOptions as ProvidesDefaultOptionsContract;
use Viserio\Component\Contract\OptionsResolver\RequiresComponentConfig as RequiresComponentConfigContract;
use Viserio\Component\Contract\Routing\Router as RouterContract;
use Viserio\Component\Routing\Command\RouteListCommand;

class ConsoleCommandsServiceProvider implements
    ServiceProviderInterface,
    RequiresComponentConfigContract,
    ProvidesDefaultOptionsContract
{
    /**
     * {@inheritdoc}
     */
    public function getFactories(): array
    {
        return [];
    }

    /**
     * {@inheritdoc}
     */
    public function getExtensions(): array
    {
        return [
            Application::class => [self::class, 'extendConsole'],
        ];
    }

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

    /**
     * {@inheritdoc}
     */
    public static function getDefaultOptions(): array
    {
        return [
            'lazily_commands' => [
                'route:table' => RouteListCommand::class,
            ],
        ];
    }

    /**
     * Extend viserio console with commands.
     *
     * @param \Psr\Container\ContainerInterface           $container
     * @param null|\Viserio\Component\Console\Application $console
     *
     * @return null|\Viserio\Component\Console\Application
     */
    public static function extendConsole(ContainerInterface $container, ?Application $console = null): ?Application
    {
        if ($console !== null && $container->has(RouterContract::class)) {
            $console->addCommands([
                new RouteListCommand($container->get(RouterContract::class)),
            ]);
        }

        return $console;
    }
}


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

use Interop\Container\ServiceProviderInterface;
use Psr\Container\ContainerInterface;
use Viserio\Component\Contract\Filesystem\Filesystem as FilesystemContract;
use Viserio\Component\Filesystem\Filesystem;

class FilesServiceProvider implements ServiceProviderInterface
{
    /**
     * {@inheritdoc}
     */
    public function getFactories(): array
    {
        return [
            FilesystemContract::class => static function (): Filesystem {
                return new Filesystem();
            },
            'files' => static function (ContainerInterface $container) {
                return $container->get(Filesystem::class);
            },
            Filesystem::class => static function (ContainerInterface $container) {
                return $container->get(FilesystemContract::class);
            },
        ];
    }

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


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

use Interop\Container\ServiceProviderInterface;
use Psr\Container\ContainerInterface;
use Viserio\Component\Config\Command\ConfigCacheCommand;
use Viserio\Component\Config\Command\ConfigClearCommand;
use Viserio\Component\Console\Application;
use Viserio\Component\Contract\OptionsResolver\ProvidesDefaultOptions as ProvidesDefaultOptionsContract;
use Viserio\Component\Contract\OptionsResolver\RequiresComponentConfig as RequiresComponentConfigContract;

class ConsoleCommandsServiceProvider implements
    ServiceProviderInterface,
    RequiresComponentConfigContract,
    ProvidesDefaultOptionsContract
{
    /**
     * {@inheritdoc}
     */
    public function getFactories(): array
    {
        return [];
    }

    /**
     * {@inheritdoc}
     */
    public function getExtensions(): array
    {
        return [
            Application::class => [self::class, 'extendConsole'],
        ];
    }

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

    /**
     * {@inheritdoc}
     */
    public static function getDefaultOptions(): array
    {
        return [
            'lazily_commands' => [
                'config:cache' => ConfigCacheCommand::class,
                'config:clear' => ConfigClearCommand::class,
            ],
        ];
    }

    /**
     * Extend viserio console with commands.
     *
     * @param \Psr\Container\ContainerInterface           $container
     * @param null|\Viserio\Component\Console\Application $console
     *
     * @return null|\Viserio\Component\Console\Application
     */
    public static function extendConsole(ContainerInterface $container, ?Application $console = null): ?Application
    {
        if ($console !== null) {
            $console->addCommands([
                new ConfigCacheCommand(),
                new ConfigClearCommand(),
            ]);
        }

        return $console;
    }
}


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

use Interop\Container\ServiceProviderInterface;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\UriFactoryInterface;
use Viserio\Component\Contract\Events\EventManager as EventManagerContract;
use Viserio\Component\Contract\Routing\Dispatcher as DispatcherContract;
use Viserio\Component\Contract\Routing\Router as RouterContract;
use Viserio\Component\Contract\Routing\UrlGenerator as UrlGeneratorContract;
use Viserio\Component\Pipeline\Pipeline;
use Viserio\Component\Routing\Dispatcher\MiddlewareBasedDispatcher;
use Viserio\Component\Routing\Dispatcher\SimpleDispatcher;
use Viserio\Component\Routing\Generator\UrlGenerator;
use Viserio\Component\Routing\Router;

class RoutingServiceProvider implements ServiceProviderInterface
{
    /**
     * {@inheritdoc}
     */
    public function getFactories(): array
    {
        return [
            DispatcherContract::class => [self::class, 'createRouteDispatcher'],
            RouterContract::class     => [self::class, 'createRouter'],
            'route'                   => static function (ContainerInterface $container) {
                return $container->get(Router::class);
            },
            'router' => static function (ContainerInterface $container) {
                return $container->get(RouterContract::class);
            },
            Router::class => static function (ContainerInterface $container) {
                return $container->get(RouterContract::class);
            },
            UrlGeneratorContract::class => [self::class, 'createUrlGenerator'],
            UrlGenerator::class         => static function (ContainerInterface $container) {
                return $container->get(UrlGeneratorContract::class);
            },
        ];
    }

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

    /**
     * Create a route dispatcher instance.
     *
     * @param \Psr\Container\ContainerInterface $container
     * @param null|callable                     $getPrevious
     *
     * @return \Viserio\Component\Contract\Routing\Dispatcher
     */
    public static function createRouteDispatcher(
        ContainerInterface $container,
        ?callable $getPrevious = null
    ): DispatcherContract {
        // @codeCoverageIgnoreStart
        if (\is_callable($getPrevious)) {
            $dispatcher = $getPrevious();
        } elseif (\class_exists(Pipeline::class)) {
            $dispatcher = new MiddlewareBasedDispatcher();
        } else {
            $dispatcher = new SimpleDispatcher();
        }
        // @codeCoverageIgnoreStop

        if ($container->has(EventManagerContract::class)) {
            $dispatcher->setEventManager($container->get(EventManagerContract::class));
        }

        return $dispatcher;
    }

    /**
     * Create a router instance.
     *
     * @param \Psr\Container\ContainerInterface $container
     *
     * @return \Viserio\Component\Contract\Routing\Router
     */
    public static function createRouter(ContainerInterface $container): RouterContract
    {
        $router = new Router($container->get(DispatcherContract::class));

        $router->setContainer($container);

        return $router;
    }

    /**
     * Create a url generator instance.
     *
     * @param \Psr\Container\ContainerInterface $container
     *
     * @return null|\Viserio\Component\Contract\Routing\UrlGenerator
     */
    public static function createUrlGenerator(ContainerInterface $container): ?UrlGeneratorContract
    {
        if (! $container->has(UriFactoryInterface::class)) {
            return null;
        }

        return new UrlGenerator(
            $container->get(RouterContract::class)->getRoutes(),
            $container->get(ServerRequestInterface::class),
            $container->get(UriFactoryInterface::class)
        );
    }
}


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

use Closure;
use Viserio\Component\Contract\Foundation\Environment as EnvironmentContract;

class EnvironmentDetector implements EnvironmentContract
{
    /**
     * {@inheritdoc}
     */
    public function detect(Closure $callback, array $consoleArgs = null): string
    {
        if ($consoleArgs !== null) {
            return $this->detectConsoleEnvironment($callback, $consoleArgs);
        }

        return $this->detectWebEnvironment($callback);
    }

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

    /**
     * {@inheritdoc}
     */
    public function getVersion(): string
    {
        return \PHP_VERSION;
    }

    /**
     * {@inheritdoc}
     */
    public function hasXdebug(): bool
    {
        return $this->isPHP() && \extension_loaded('xdebug');
    }

    /**
     * {@inheritdoc}
     */
    public function isPHP(): bool
    {
        return ! \defined('HHVM_VERSION');
    }

    /**
     * {@inheritdoc}
     */
    public function runningInConsole(): bool
    {
        return \in_array(\PHP_SAPI, ['cli', 'phpdbg'], true);
    }

    /**
     * Set the application environment from command-line arguments.
     *
     * @param \Closure $callback
     * @param array    $args
     *
     * @return string
     */
    protected function detectConsoleEnvironment(Closure $callback, array $args): string
    {
        // First we will check if an environment argument was passed via console arguments
        // and if it was that automatically overrides as the environment. Otherwise, we
        // will check the environment as a "web" request like a typical HTTP request.
        $value = $this->getEnvironmentArgument($args);

        if ($value !== null) {
            $arr = \array_slice(\explode('=', $value), 1);

            return \reset($arr);
        }

        return $this->detectWebEnvironment($callback);
    }

    /**
     * Set the application environment for a web request.
     *
     * @param \Closure $callback
     *
     * @return string
     */
    protected function detectWebEnvironment(Closure $callback): string
    {
        return $callback();
    }

    /**
     * Get the environment argument from the console.
     *
     * @param array $args
     *
     * @return null|string
     */
    protected function getEnvironmentArgument(array $args): ?string
    {
        $callback = static function ($v) {
            return self::startsWith($v, '--env');
        };

        foreach ($args as $key => $value) {
            if ($callback($value)) {
                return $value;
            }
        }

        return null;
    }

    /**
     * Determine if a given string starts with a given substring.
     *
     * @param string $haystack
     * @param string $needle
     *
     * @return bool
     */
    private static function startsWith(string $haystack, string $needle): bool
    {
        return $needle !== '' && \mb_strpos($haystack, $needle) === 0;
    }
}


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

use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Viserio\Component\Contract\Profiler\Profiler as ProfilerContract;
use Viserio\Component\Contract\Session\Store as StoreContract;
use Viserio\Component\Routing\AbstractController;

class AssetController extends AbstractController
{
    /**
     * Response factory instance.
     *
     * @var \Psr\Http\Message\ResponseFactoryInterface
     */
    protected $responseFactory;

    /**
     * Stream factory instance.
     *
     * @var \Psr\Http\Message\StreamFactoryInterface
     */
    protected $streamFactory;

    /**
     * Profiler instance.
     *
     * @var \Viserio\Component\Contract\Profiler\Profiler
     */
    protected $profiler;

    /**
     * Create a new AssetController instance.
     *
     * @param \Psr\Http\Message\ServerRequestInterface      $serverRequest
     * @param \Psr\Http\Message\ResponseFactoryInterface    $responseFactory
     * @param \Psr\Http\Message\StreamFactoryInterface      $streamFactory
     * @param \Viserio\Component\Contract\Profiler\Profiler $profiler
     */
    public function __construct(
        ServerRequestInterface $serverRequest,
        ResponseFactoryInterface $responseFactory,
        StreamFactoryInterface $streamFactory,
        ProfilerContract $profiler
    ) {
        $this->responseFactory = $responseFactory;
        $this->streamFactory   = $streamFactory;
        $this->profiler        = $profiler;
        $session               = $serverRequest->getAttribute('session');

        if ($session instanceof StoreContract) {
            $session->reflash();
        }
    }

    /**
     * Return the javascript for the Debugbar.
     *
     * @return \Psr\Http\Message\ResponseInterface
     */
    public function js(): ResponseInterface
    {
        $renderer = $this->profiler->getAssetsRenderer();

        $stream = $this->streamFactory->createStream(
            $renderer->dumpAssetsToString('js')
        );

        $response = $this->responseFactory->createResponse();
        $response = $response->withHeader('content-type', 'text/js');

        return $response->withBody($stream);
    }

    /**
     * Return the stylesheets for the Debugbar.
     *
     * @return \Psr\Http\Message\ResponseInterface
     */
    public function css(): ResponseInterface
    {
        $renderer = $this->profiler->getAssetsRenderer();

        $stream = $this->streamFactory->createStream(
            $renderer->dumpAssetsToString('css')
        );

        $response = $this->responseFactory->createResponse();
        $response = $response->withHeader('content-type', 'text/css');

        return $response->withBody($stream);
    }
}


<?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;
use Yosymfony\Toml\Exception\DumpException as YosymfonyDumpException;
use Yosymfony\Toml\TomlBuilder;

class TomlDumper implements DumperContract
{
    /**
     * {@inheritdoc}
     *
     *    array[]
     *        array['key']
     *            ['key']                string|int|array|\Datetime
     *            or
     *        array['key']
     *            array[]
     *                ['key']            string|int|array|\Datetime
     *                array['key']
     *                    array[]
     *                        ['key']    string|int|array|\Datetime
     */
    public function dump(array $data): string
    {
        try {
            $builder = $this->fromArray($data, new TomlBuilder());
        } catch (YosymfonyDumpException $exception) {
            throw new DumpException(
                $exception->getMessage(),
                $exception->getCode(),
                $exception
            );
        }

        return $builder->getTomlString();
    }

    /**
     * Build toml file from given array.
     *
     * @param array                       $data
     * @param \Yosymfony\Toml\TomlBuilder $builder
     * @param string                      $parent
     *
     * @return \Yosymfony\Toml\TomlBuilder
     */
    private function fromArray(array $data, TomlBuilder $builder, string $parent = ''): TomlBuilder
    {
        foreach ($data as $key => $value) {
            if (\is_array($value)) {
                if ($this->hasStringKeys($value)) {
                    $key = $parent !== '' ? "${parent}.${key}" : $key;

                    if (\mb_strpos($key, '.') !== false) {
                        $builder->addTable($key);
                    }

                    $builder = $this->fromArray($value, $builder, $key);
                } elseif ($this->onlyArrays($value)) {
                    $builder = $this->processArrayOfArrays($value, $key, $builder);
                } else {
                    // Plain array.
                    $builder->addValue($key, $value);
                }
            } else {
                // Simple key/value.
                $builder->addValue($key, $value);
            }
        }

        return $builder;
    }

    /**
     * Run through all arrays.
     *
     * @param array                       $values
     * @param string                      $parent
     * @param \Yosymfony\Toml\TomlBuilder $builder
     *
     * @return \Yosymfony\Toml\TomlBuilder
     */
    private function processArrayOfArrays(array $values, string $parent, TomlBuilder $builder): TomlBuilder
    {
        $array = [];

        foreach ($values as $value) {
            if ($this->hasStringKeys($value)) {
                $builder->addArrayOfTable($parent);

                foreach ($value as $key => $val) {
                    if (\is_array($val)) {
                        $builder = $this->processArrayOfArrays($val, "${parent}.${key}", $builder);
                    } else {
                        $builder->addValue($key, $val);
                    }
                }
            } else {
                $array[] = $value;
            }
        }

        if (\count($array) !== 0) {
            $builder->addValue($parent, $array);
        }

        return $builder;
    }

    /**
     * Check if array has string keys.
     *
     * @param array $array
     *
     * @return bool
     */
    private function hasStringKeys(array $array): bool
    {
        return \count(\array_filter(\array_keys($array), '\is_string')) > 0;
    }

    /**
     * Check if array has only arrays.
     *
     * @param array $array
     *
     * @return bool
     */
    private function onlyArrays(array $array): bool
    {
        return \count(\array_filter(\array_values($array), '\is_array')) === \count($array);
    }
}


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

use ReflectionFunction;
use ReflectionMethod;
use ReflectionParameter;
use Twig\Environment;
use Twig\Loader\FilesystemLoader;
use UnexpectedValueException;
use Viserio\Component\Console\Command\AbstractCommand;

/**
 * Lists twig functions, filters, globals and tests present in the current project.
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 * @copyright Copyright (c) 2004-2017 Fabien Potencier
 */
class DebugCommand extends AbstractCommand
{
    /**
     * {@inheritdoc}
     */
    protected static $defaultName = 'twig:debug';

    /**
     * {@inheritdoc}
     */
    protected $signature = 'twig:debug
        [--filter= : Show details for all entries matching this filter.]
        [--format=txt : The output format. Supports `txt` or `json`.]
    ';

    /**
     * {@inheritdoc}
     */
    protected $description = 'Shows a list of twig functions, filters, globals and tests';

    /**
     * A twig instance.
     *
     * @var \Twig\Environment
     */
    private $environment;

    /**
     * Create a DebugCommand instance.
     *
     * @param \Twig\Environment $environment
     */
    public function __construct(Environment $environment)
    {
        parent::__construct();

        $this->environment = $environment;
    }

    /**
     * {@inheritdoc}
     */
    public function handle(): int
    {
        $types = ['functions', 'filters', 'tests', 'globals'];

        if ($this->option('format') === 'json') {
            $data = [];

            foreach ($types as $type) {
                foreach ($this->environment->{'get' . \ucfirst($type)}() as $name => $entity) {
                    $data[$type][$name] = $this->getMetadata($type, $entity);
                }
            }

            $data['tests']        = \array_keys($data['tests']);
            $data['loader_paths'] = $this->getLoaderPaths($this->environment);

            $this->line(\json_encode($data));

            return 0;
        }

        $filter = $this->option('filter');

        foreach ($types as $index => $type) {
            $items = [];

            foreach ($this->environment->{'get' . \ucfirst($type)}() as $name => $entity) {
                if (! (bool) $filter || \mb_strpos($name, $filter) !== false) {
                    $items[$name] = $name . $this->getPrettyMetadata($type, $entity);
                }
            }

            if (\count($items) === 0) {
                continue;
            }

            $this->output->section(\ucfirst($type));

            \ksort($items);

            $this->output->listing($items);
        }

        $this->output->section('Configured Paths');
        $this->output->table(['Namespace', 'Paths'], $this->buildTableRows($this->getLoaderPaths($this->environment)));

        return 0;
    }

    /**
     * Get twig metadata.
     *
     * @param string $type
     * @param object $entity
     *
     * @throws \UnexpectedValueException
     * @throws \ReflectionException
     *
     * @return mixed
     */
    private function getMetadata(string $type, object $entity)
    {
        if ($type === 'globals') {
            return $entity;
        }

        if ($type === 'tests') {
            return null;
        }

        $isFilters = $type === 'filters';

        if ($type === 'functions' || $isFilters) {
            $cb = $entity->getCallable();

            if ($cb === null) {
                return null;
            }

            if (\is_array($cb)) {
                if (! \method_exists($cb[0], $cb[1])) {
                    return null;
                }

                $refl = new ReflectionMethod($cb[0], $cb[1]);
            } elseif (\is_object($cb) && \method_exists($cb, '__invoke')) {
                $refl = new ReflectionMethod($cb, '__invoke');
            } elseif (\function_exists($cb)) {
                $refl = new ReflectionFunction($cb);
            } elseif (\is_string($cb) && \preg_match('{^(.+)::(.+)$}', $cb, $m) && \method_exists($m[1], $m[2])) {
                $refl = new ReflectionMethod($m[1], $m[2]);
            } else {
                throw new UnexpectedValueException('Unsupported callback type.');
            }

            // filter out context/environment args
            $args = \array_filter($refl->getParameters(), static function (ReflectionParameter $param) use ($entity) {
                if ($entity->needsContext() && $param->getName() === 'context') {
                    return false;
                }

                return ! $param->getClass() || $param->getClass()->getName() !== 'Environment';
            });

            // format args
            $args = \array_map(static function (ReflectionParameter $param) {
                if ($param->isDefaultValueAvailable()) {
                    return $param->getName() . ' = ' . \json_encode($param->getDefaultValue());
                }

                return $param->getName();
            }, $args);

            if ($isFilters) {
                // remove the value the filter is applied on
                \array_shift($args);
            }

            return $args;
        }

        return null;
    }

    /**
     * Transform metadata.
     *
     * @param string $type
     * @param object $entity
     *
     * @return string
     */
    private function getPrettyMetadata(string $type, object $entity): string
    {
        if ($type === 'tests') {
            return '';
        }

        try {
            $meta = $this->getMetadata($type, $entity);

            if ($meta === null) {
                return '(unknown?)';
            }
        } catch (UnexpectedValueException $e) {
            return ' <error>' . $e->getMessage() . '</error>';
        }

        if ($type === 'globals') {
            if (\is_object($meta)) {
                return ' = object(' . \get_class($meta) . ')';
            }

            return ' = ' . \mb_substr(@\json_encode($meta), 0, 50);
        }

        if ($type === 'functions') {
            return '(' . \implode(', ', $meta) . ')';
        }

        if ($type === 'filters') {
            return \is_array($meta) ? '(' . \implode(', ', $meta) . ')' : '';
        }

        return '';
    }

    /**
     * Get the loader paths.
     *
     * @param \Twig\Environment $twig
     *
     * @return array
     */
    private function getLoaderPaths(Environment $twig): array
    {
        /** @var \Twig\Loader\FilesystemLoader $loader */
        $loader = $twig->getLoader();

        if (! $loader instanceof FilesystemLoader) {
            return [];
        }

        $loaderPaths = [];

        foreach ($loader->getNamespaces() as $namespace) {
            $paths = $loader->getPaths($namespace);

            if (FilesystemLoader::MAIN_NAMESPACE === $namespace) {
                $namespace = '(None)';
            } else {
                $namespace = '@' . $namespace;
            }

            $loaderPaths[$namespace] = $paths;
        }

        return $loaderPaths;
    }

    /**
     * Build configured path table.
     *
     * @param array $loaderPaths
     *
     * @return array
     */
    private function buildTableRows(array $loaderPaths): array
    {
        $rows             = [];
        $firstNamespace   = true;
        $prevHasSeparator = false;

        foreach ($loaderPaths as $namespace => $paths) {
            if (! $firstNamespace && ! $prevHasSeparator && \count($paths) > 1) {
                $rows[] = ['', ''];
            }

            $firstNamespace = false;

            foreach ($paths as $path) {
                $rows[]    = [$namespace, '- ' . $path];
                $namespace = '';
            }

            if (\count($paths) > 1) {
                $rows[]           = ['', ''];
                $prevHasSeparator = true;
            } else {
                $prevHasSeparator = false;
            }
        }

        if ($prevHasSeparator) {
            \array_pop($rows);
        }

        return $rows;
    }
}