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

use Viserio\Component\Contract\Routing\Route;
use Viserio\Component\Contract\Routing\Route as RouteContract;
use Viserio\Component\Contract\Routing\Router as RouterContract;
use Viserio\Component\Support\Str;

class ResourceRegistrar
{
    /**
     * The router instance.
     *
     * @var \Viserio\Component\Contract\Routing\Router
     */
    protected $router;

    /**
     * The default actions for a resourceful controller.
     *
     * @var array
     */
    protected static $resourceDefaults = [
        'index',
        'create',
        'store',
        'show',
        'edit',
        'update',
        'destroy',
    ];

    /**
     * The parameters set for this resource instance.
     *
     * @var array
     */
    protected $parameters = [];

    /**
     * Singular global parameters.
     *
     * @var bool
     */
    protected static $singularParameters = true;

    /**
     * The global parameter mapping.
     *
     * @var array
     */
    protected static $parameterMap = [];

    /**
     * The verbs used in the resource URIs.
     *
     * @var array
     */
    protected static $verbs = [
        'create' => 'create',
        'edit'   => 'edit',
    ];

    /**
     * Create a new resource registrar instance.
     *
     * @param \Viserio\Component\Contract\Routing\Router $router
     */
    public function __construct(RouterContract $router)
    {
        $this->router = $router;
    }

    /**
     * Get the global parameter map.
     *
     * @return array
     */
    public static function getParameters(): array
    {
        return static::$parameterMap;
    }

    /**
     * Set the global parameter mapping.
     *
     * @param array $parameters
     *
     * @return void
     */
    public static function setParameters(array $parameters = []): void
    {
        static::$parameterMap = $parameters;
    }

    /**
     * Set or unset the unmapped global parameters to singular.
     *
     * @param bool $singular
     *
     * @return void
     */
    public static function singularParameters(bool $singular = true): void
    {
        static::$singularParameters = $singular;
    }

    /**
     * Get the action verbs used in the resource URIs.
     *
     * @return array
     */
    public static function getVerbs(): array
    {
        return static::$verbs;
    }

    /**
     * Set the action verbs used in the resource URIs.
     *
     * @param array $verbs
     *
     * @return void
     */
    public static function setVerbs(array $verbs): void
    {
        static::$verbs = \array_merge(static::$verbs, $verbs);
    }

    /**
     * Route a resource to a controller.
     *
     * @param string $name
     * @param string $controller
     * @param array  $options
     *
     * @return void
     */
    public function register(string $name, string $controller, array $options = []): void
    {
        if (isset($options['parameters']) && \count($this->parameters) === 0) {
            $this->parameters = (array) $options['parameters'];
        }

        // If the resource name contains a slash, we will assume the developer wishes to
        // register these resource routes with a prefix so we will set that up out of
        // the box so they don't have to mess with it. Otherwise, we will continue.
        if (\strpos($name, '/') !== false) {
            $this->prefixedResource($name, $controller, $options);

            return;
        }

        // We need to extract the base resource from the resource name.
        $baseResource = \explode('.', $name);
        $resource     = \end($baseResource);

        // Wildcards for a single or nested resource may be overridden using the wildcards option.
        // Overrides are performed by matching the wildcards key with the resource name. If a key
        // matches a resource name, the value of the wildcard is used instead of the resource name.
        if (isset($options['wildcards'][$resource])) {
            $resource = $options['wildcards'][$resource];
        }

        // Nested resources are supported in the framework, but we need to know what
        // name to use for a place-holder on the route wildcards, which should be
        // the base resources.
        $base = $this->getResourceWildcard($resource);

        $defaults = self::$resourceDefaults;

        foreach ($this->getResourceMethods($defaults, $options) as $m) {
            $this->{'addResource' . \ucfirst($m)}($name, $base, $controller, $options);
        }
    }

    /**
     * Get the base resource URI for a given resource.
     *
     * @param string $resource
     * @param array  $options
     *
     * @return string
     */
    public function getResourceUri(string $resource, array $options): string
    {
        if (\strpos($resource, '.') === false) {
            return $resource;
        }

        // Once we have built the base URI, we'll remove the parameter holder for this
        // base resource name so that the individual route adders can suffix these
        // paths however they need to, as some do not have any parameters at all.
        $segments = \explode('.', $resource);

        $uri = $this->getNestedResourceUri($segments, $options);

        $resource = \end($segments);

        if (isset($options['wildcards'][$resource])) {
            $resource = $options['wildcards'][$resource];
        }

        return \str_replace('/{' . $this->getResourceWildcard($resource) . '}', '', $uri);
    }

    /**
     * Format a resource parameter for usage.
     *
     * @param string $value
     *
     * @return string
     */
    public function getResourceWildcard(string $value): string
    {
        if (isset($this->parameters[$value])) {
            $value = $this->parameters[$value];
        } elseif (isset(static::$parameterMap[$value])) {
            $value = static::$parameterMap[$value];
        } elseif (static::$singularParameters || \array_search('singular', $this->parameters, true) !== false) {
            $value = Str::singular($value);
        }

        return \str_replace('-', '_', $value);
    }

    /**
     * Build a set of prefixed resource routes.
     *
     * @param string $name
     * @param string $controller
     * @param array  $options
     *
     * @return void
     */
    protected function prefixedResource(string $name, string $controller, array $options): void
    {
        [$name, $prefix] = $this->getResourcePrefix($name);

        // We need to extract the base resource from the resource name. Nested resources
        // are supported in the framework, but we need to know what name to use for a
        // place-holder on the route parameters, which should be the base resources.
        $callback = static function ($me) use ($name, $controller, $options): void {
            $me->resource($name, $controller, $options);
        };

        $this->router->group(\compact('prefix'), $callback);
    }

    /**
     * Extract the resource and prefix from a resource name.
     *
     * @param string $name
     *
     * @return array
     */
    protected function getResourcePrefix(string $name): array
    {
        $segments = \explode('/', $name);

        // To get the prefix, we will take all of the name segments and implode them on
        // a slash. This will generate a proper URI prefix for us. Then we take this
        // last segment, which will be considered the final resources name we use.
        $prefix = \implode('/', \array_slice($segments, 0, -1));

        return [\end($segments), $prefix];
    }

    /**
     * Get the applicable resource methods.
     *
     * @param array $defaults
     * @param array $options
     *
     * @return array
     */
    protected function getResourceMethods(array $defaults, array $options): array
    {
        if (isset($options['only'])) {
            return \array_intersect($defaults, (array) $options['only']);
        }

        if (isset($options['except'])) {
            return \array_diff($defaults, (array) $options['except']);
        }

        return $defaults;
    }

    /**
     * Add the index method for a resourceful route.
     *
     * @param string $name
     * @param string $base
     * @param string $controller
     * @param array  $options
     *
     * @return Route
     */
    protected function addResourceIndex(string $name, string $base, string $controller, array $options): Route
    {
        $uri = $this->getResourceUri($name, $options);

        $action = $this->getResourceAction($name, $controller, 'index', $options);

        return $this->router->get($uri, $action);
    }

    /**
     * Add the create method for a resourceful route.
     *
     * @param string $name
     * @param string $base
     * @param string $controller
     * @param array  $options
     *
     * @return Route
     */
    protected function addResourceCreate(string $name, string $base, string $controller, array $options): Route
    {
        $uri = $this->getResourceUri($name, $options) . '/' . static::$verbs['create'];

        $action = $this->getResourceAction($name, $controller, 'create', $options);

        return $this->router->get($uri, $action);
    }

    /**
     * Add the store method for a resourceful route.
     *
     * @param string $name
     * @param string $base
     * @param string $controller
     * @param array  $options
     *
     * @return Route
     */
    protected function addResourceStore(string $name, string $base, string $controller, array $options): RouteContract
    {
        $uri = $this->getResourceUri($name, $options);

        $action = $this->getResourceAction($name, $controller, 'store', $options);

        return $this->router->post($uri, $action);
    }

    /**
     * Add the show method for a resourceful route.
     *
     * @param string $name
     * @param string $base
     * @param string $controller
     * @param array  $options
     *
     * @return Route
     */
    protected function addResourceShow(string $name, string $base, string $controller, array $options): RouteContract
    {
        $uri = $this->getResourceUri($name, $options) . '/{' . $base . '}';

        $action = $this->getResourceAction($name, $controller, 'show', $options);

        return $this->router->get($uri, $action);
    }

    /**
     * Add the edit method for a resourceful route.
     *
     * @param string $name
     * @param string $base
     * @param string $controller
     * @param array  $options
     *
     * @return Route
     */
    protected function addResourceEdit(string $name, string $base, string $controller, array $options): RouteContract
    {
        $uri = $this->getResourceUri($name, $options) . '/{' . $base . '}/' . static::$verbs['edit'];

        $action = $this->getResourceAction($name, $controller, 'edit', $options);

        return $this->router->get($uri, $action);
    }

    /**
     * Add the update method for a resourceful route.
     *
     * @param string $name
     * @param string $base
     * @param string $controller
     * @param array  $options
     *
     * @return Route
     */
    protected function addResourceUpdate(string $name, string $base, string $controller, array $options): RouteContract
    {
        $uri = $this->getResourceUri($name, $options) . '/{' . $base . '}';

        $action = $this->getResourceAction($name, $controller, 'update', $options);

        return $this->router->match(['PUT', 'PATCH'], $uri, $action);
    }

    /**
     * Add the destroy method for a resourceful route.
     *
     * @param string $name
     * @param string $base
     * @param string $controller
     * @param array  $options
     *
     * @return Route
     */
    protected function addResourceDestroy(string $name, string $base, string $controller, array $options): RouteContract
    {
        $uri = $this->getResourceUri($name, $options) . '/{' . $base . '}';

        $action = $this->getResourceAction($name, $controller, 'destroy', $options);

        return $this->router->delete($uri, $action);
    }

    /**
     * Get the URI for a nested resource segment array.
     *
     * @param array $segments
     * @param array $options
     *
     * @return string
     */
    protected function getNestedResourceUri(array $segments, array $options): string
    {
        // We will spin through the segments and create a place-holder for each of the
        // resource segments, as well as the resource itself. Then we should get an
        // entire string for the resource URI that contains all nested resources.
        return \implode('/', \array_map(function ($s) use ($options) {
            $wildcard = $s;

            //If a wildcard for a resource has been set to be overridden
            if (isset($options['wildcards'][$s])) {
                $wildcard = $options['wildcards'][$s];
            }

            return $s . '/{' . $this->getResourceWildcard($wildcard) . '}';
        }, $segments));
    }

    /**
     * Get the action array for a resource route.
     *
     * @param string $resource
     * @param string $controller
     * @param string $method
     * @param array  $options
     *
     * @return array
     */
    protected function getResourceAction(string $resource, string $controller, string $method, array $options): array
    {
        $name = $this->getResourceRouteName($resource, $method, $options);

        $action = ['as' => $name, 'uses' => $controller . '@' . $method];

        if (isset($options['middleware'])) {
            $action['middleware'] = $options['middleware'];
        }

        if (isset($options['bypass'])) {
            $action['bypass'] = $options['bypass'];
        }

        return $action;
    }

    /**
     * Get the name for a given resource.
     *
     * @param string $resource
     * @param string $method
     * @param array  $options
     *
     * @return string
     */
    protected function getResourceRouteName(string $resource, string $method, array $options): string
    {
        $name = $resource;

        // If the names array has been provided to us we will check for an entry in the
        // array first. We will also check for the specific method within this array
        // so the names may be specified on a more "granular" level using methods.
        if (isset($options['names'])) {
            if (\is_string($options['names'])) {
                $name = $options['names'];
            } elseif (isset($options['names'][$method])) {
                return $options['names'][$method];
            }
        }

        // If a global prefix has been assigned to all names for this resource, we will
        // grab that so we can prepend it onto the name when we create this name for
        // the resource action. Otherwise we'll just use an empty string for here.
        $prefix = isset($options['as']) ? $options['as'] . '.' : '';

        return \trim(\sprintf('%s%s.%s', $prefix, $name, $method), '.');
    }
}


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

use Closure;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Viserio\Component\Contract\Container\Factory as FactoryContract;
use Viserio\Component\Contract\Routing\Exception\RuntimeException;
use Viserio\Component\Pipeline\Pipeline as BasePipeline;

class Pipeline extends BasePipeline
{
    /**
     * {@inheritdoc}
     */
    protected $method = 'process';

    /**
     * {@inheritdoc}
     */
    protected function getSlice(): Closure
    {
        return function ($stack, $pipe) {
            return function ($passable) use ($stack, $pipe) {
                $slice = parent::getSlice();

                $callable = $slice($this->getRequestHandlerMiddleware($stack), $pipe);

                return $callable($passable);
            };
        };
    }

    /**
     * Resolve from container.
     *
     * @param mixed  $traveler
     * @param mixed  $stack
     * @param string $stage
     *
     * @throws \Viserio\Component\Contract\Routing\Exception\RuntimeException
     *
     * @return mixed
     */
    protected function sliceThroughContainer($traveler, $stack, string $stage)
    {
        [$name, $parameters] = $this->parseStageString($stage);
        $parameters          = \array_merge([$traveler, $stack], $parameters);
        $class               = null;

        if ($this->container->has($name)) {
            $class = $this->container->get($name);
        } elseif ($this->container instanceof FactoryContract) {
            $class = $this->container->resolve($name);
        } else {
            throw new RuntimeException(\sprintf('Class [%s] is not being managed by the container.', $name));
        }

        return $this->getInvoker()->call([$class, $this->method], $parameters);
    }

    /**
     * Private delegate callable middleware for the pipe.
     *
     * @param callable $middleware
     *
     * @return \Psr\Http\Server\RequestHandlerInterface
     */
    private function getRequestHandlerMiddleware(callable $middleware): RequestHandlerInterface
    {
        return new class($middleware) implements RequestHandlerInterface {
            /**
             * @var callable
             */
            private $middleware;

            /**
             * Create a new delegate callable middleware instance.
             *
             * @param callable $middleware
             */
            public function __construct(callable $middleware)
            {
                $this->middleware = $middleware;
            }

            /**
             * {@inheritdoc}
             */
            public function handle(ServerRequestInterface $request): ResponseInterface
            {
                return \call_user_func($this->middleware, $request);
            }
        };
    }
}


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

use Symfony\Component\Finder\Finder;
use Twig\Environment;
use Viserio\Bridge\Twig\Command\LintCommand as BaseLintCommand;
use Viserio\Component\Contract\OptionsResolver\ProvidesDefaultOptions as ProvidesDefaultOptionsContract;
use Viserio\Component\Contract\OptionsResolver\RequiresComponentConfig as RequiresComponentConfigContract;
use Viserio\Component\Contract\View\Finder as FinderContract;
use Viserio\Component\OptionsResolver\Traits\OptionsResolverTrait;

class LintCommand extends BaseLintCommand implements RequiresComponentConfigContract, ProvidesDefaultOptionsContract
{
    use OptionsResolverTrait;

    /**
     * {@inheritdoc}
     */
    protected $signature = 'lint:twig
        [--files=* : Lint multiple files. Relative to the view path.]
        [--directories=* : Lint multiple directories. Relative to the view path.]
        [--format=txt : The output format. Supports `txt` or `json`.]
    ';

    /**
     * Resolved options.
     *
     * @var array
     */
    private $resolvedOptions;

    /**
     * A view finder instance.
     *
     * @var \Viserio\Component\Contract\View\Finder
     */
    private $finder;

    /**
     * Create a DebugCommand instance.
     *
     * @param \Twig\Environment                       $environment
     * @param \Viserio\Component\Contract\View\Finder $finder
     * @param array|\ArrayAccess                      $config
     */
    public function __construct(Environment $environment, FinderContract $finder, $config)
    {
        parent::__construct($environment);

        $this->finder          = $finder;
        $this->resolvedOptions = self::resolveOptions($config);
    }

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

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

    /**
     * {@inheritdoc}
     */
    protected function getFinder(array $paths, string $file = null): iterable
    {
        return Finder::create()
            ->files()
            ->in($paths)
            ->name(($file === null ? '*.' : $file . '.') . $this->resolvedOptions['engines']['twig']['file_extension'])
            ->getIterator();
    }

    /**
     * {@inheritdoc}
     */
    protected function getFiles(array $files, array $directories): array
    {
        // Get files from passed in options
        $search            = [];
        $paths             = $this->finder->getPaths();
        $hints             = $this->finder->getHints();
        $searchDirectories = [];

        if (\is_array($hints) && \count($hints) !== 0) {
            $paths = \array_reduce($hints, static function ($package, $paths) {
                return \array_merge($paths, $package);
            }, $paths);
        }

        if (\count($directories) !== 0) {
            foreach ($directories as $directory) {
                foreach ($paths as $path) {
                    $path = $path . \DIRECTORY_SEPARATOR . $directory;

                    if (\is_dir($path)) {
                        $searchDirectories[] = $path;
                    } else {
                        $this->warn('Path "' . $path . '" is not a directory.');
                    }
                }
            }

            if (\count($searchDirectories) !== 0 && \count($files) === 0) {
                // Get those files from the search directory
                /** @var \SplFileObject $file */
                foreach ($this->getFinder($searchDirectories) as $file) {
                    $search[] = $file->getRealPath();
                }
            }
        }

        if (\count($files) !== 0) {
            $search = \array_merge($search, $this->findArgumentFiles($paths, $searchDirectories, $files));
        }

        // If no files passed, use the view paths
        if (\count($search) === 0) {
            /** @var \SplFileObject $file */
            foreach ($this->getFinder($paths) as $file) {
                $search[] = $file->getRealPath();
            }
        }

        return $search;
    }

    /**
     * Gets an array of argument files to lint.
     *
     * @param array $paths
     * @param array $searchDirectories
     * @param array $files
     *
     * @return array
     */
    protected function findArgumentFiles(array $paths, array $searchDirectories, array $files): array
    {
        $search = [];

        foreach ($files as $fileName) {
            if (\count($searchDirectories) !== 0) {
                /** @var \SplFileObject $file */
                foreach ($this->getFinder($searchDirectories, $fileName) as $file) {
                    $search[] = $file->getRealPath();
                }
            } else {
                /** @var \SplFileObject $file */
                foreach ($this->getFinder($paths, $fileName) as $file) {
                    $search[] = $file->getRealPath();
                }
            }
        }

        return $search;
    }
}


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

use Psr\Http\Message\ServerRequestInterface;

final class ClientIp
{
    /**
     * A server request instance.
     *
     * @var \Psr\Http\Message\ServerRequestInterface
     */
    private $serverRequest;

    /**
     * List of proxy headers inspected for the client IP address.
     *
     * @var array
     */
    private static $headersToInspect = [
        'Forwarded',
        'X-Forwarded-For',
        'X-Forwarded',
        'X-Cluster-Client-Ip',
        'Client-Ip',
    ];

    /**
     * Create ClientIp instance.
     *
     * @param \Psr\Http\Message\ServerRequestInterface $serverRequest
     */
    public function __construct(ServerRequestInterface $serverRequest)
    {
        $this->serverRequest = $serverRequest;
    }

    /**
     * Returns client IP address.
     *
     * @return null|string
     */
    public function getIpAddress(): ?string
    {
        $ipAddress    = null;
        $request      = $this->serverRequest;
        $serverParams = $request->getServerParams();

        // direct IP address
        if (isset($serverParams['REMOTE_ADDR']) && $this->isValidIpAddress($serverParams['REMOTE_ADDR'])) {
            $ipAddress = $serverParams['REMOTE_ADDR'];
        }

        foreach (self::$headersToInspect as $header) {
            if ($request->hasHeader($header)) {
                $ip = $this->getFirstIpAddressFromHeader($request, $header);

                if ($this->isValidIpAddress($ip)) {
                    $ipAddress = $ip;

                    break;
                }
            }
        }

        return $ipAddress;
    }

    /**
     * Check that a given string is a valid IP address.
     *
     * @param string $ip
     *
     * @return bool
     */
    private function isValidIpAddress(string $ip): bool
    {
        return (bool) \filter_var($ip, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4 | \FILTER_FLAG_IPV6);
    }

    /**
     * Find out the client's IP address from the headers available to us.
     *
     * @param \Psr\Http\Message\ServerRequestInterface $serverRequest
     * @param string                                   $header        Header name
     *
     * @return string
     */
    private function getFirstIpAddressFromHeader(ServerRequestInterface $serverRequest, string $header): string
    {
        $items       = \explode(',', $serverRequest->getHeaderLine($header));
        $headerValue = \trim(\reset($items));

        if (\ucfirst($header) === 'Forwarded') {
            foreach (\explode(';', $headerValue) as $headerPart) {
                if (\mb_strtolower(\mb_substr($headerPart, 0, 4)) === 'for=') {
                    $for         = \explode(']', $headerPart);
                    $headerValue = \trim(\mb_substr(\reset($for), 4), " \t\n\r\0\x0B" . '"[]');

                    break;
                }
            }
        }

        return $headerValue;
    }
}


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

use GuzzleHttp\Client;
use Swift_Mime_SimpleMessage;

class MailgunTransport extends AbstractTransport
{
    /**
     * Guzzle client instance.
     *
     * @var \GuzzleHttp\Client
     */
    protected $client;

    /**
     * The Mailgun API key.
     *
     * @var string
     */
    protected $key;

    /**
     * The Mailgun domain.
     *
     * @var string
     */
    protected $domain;

    /**
     * THe Mailgun API end-point.
     *
     * @var string
     */
    protected $url;

    /**
     * Mailgun base url.
     *
     * @see https://documentation.mailgun.com/en/latest/api-intro.html#mailgun-regions
     *
     * @var string
     */
    protected $baseUrl;

    /**
     * Create a new Mailgun transport instance.
     *
     * @param \GuzzleHttp\Client $client
     * @param string             $key
     * @param string             $domain
     * @param null|string        $baseUrl
     */
    public function __construct(Client $client, string $key, string $domain, ?string $baseUrl = null)
    {
        $this->client  = $client;
        $this->key     = $key;
        $this->baseUrl = $baseUrl ?? 'https://api.mailgun.net';

        $this->setDomain($domain);
    }

    /**
     * Get the API key being used by the transport.
     *
     * @return string
     */
    public function getKey(): string
    {
        return $this->key;
    }

    /**
     * Set the API key being used by the transport.
     *
     * @param string $key
     *
     * @return $this
     */
    public function setKey(string $key): self
    {
        $this->key = $key;

        return $this;
    }

    /**
     * Get the domain being used by the transport.
     *
     * @return string
     */
    public function getDomain(): string
    {
        return $this->domain;
    }

    /**
     * Set the domain being used by the transport.
     *
     * @param string $domain
     *
     * @return $this
     */
    public function setDomain(string $domain): self
    {
        $this->url = $this->baseUrl . '/v3/' . $domain . '/messages.mime';

        $this->domain = $domain;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null): int
    {
        $this->beforeSendPerformed($message);

        $options = ['auth' => ['api', $this->key]];

        $options['multipart'] = [
            ['name' => 'to', 'contents' => $this->getTo($message)],
            ['name' => 'cc', 'contents' => $this->getCc($message)],
            ['name' => 'bcc', 'contents' => $this->getBcc($message)],
            ['name' => 'message', 'contents' => $message->toString(), 'filename' => 'message.mime'],
        ];

        $this->client->post($this->url, $options);

        $this->sendPerformed($message);

        return $this->numberOfRecipients($message);
    }

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

    /**
     * Get the "to" payload field for the API request.
     *
     * @param \Swift_Mime_SimpleMessage $message
     *
     * @return string
     */
    protected function getTo(Swift_Mime_SimpleMessage $message): string
    {
        return $this->formatAddress($message->getTo() ?? []);
    }

    /**
     * Get the "cc" payload field for the API request.
     *
     * @param \Swift_Mime_SimpleMessage $message
     *
     * @return string
     */
    protected function getCc(Swift_Mime_SimpleMessage $message): string
    {
        return $this->formatAddress($message->getCc() ?? []);
    }

    /**
     * Get the "bcc" payload field for the API request.
     *
     * @param \Swift_Mime_SimpleMessage $message
     *
     * @return string
     */
    protected function getBcc(Swift_Mime_SimpleMessage $message): string
    {
        return $this->formatAddress($message->getBcc() ?? []);
    }

    /**
     * Get Comma-Separated Address (with name, if available) for the API request.
     *
     * @param array $contacts
     *
     * @return string
     */
    protected function formatAddress(array $contacts): string
    {
        $formatted = [];

        foreach ($contacts as $address => $display) {
            $formatted[] = $display ? $display . \sprintf('<%s>', $address) : $address;
        }

        return \implode(',', $formatted);
    }
}


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

use Psr\Http\Server\MiddlewareInterface;
use Viserio\Component\Contract\Routing\Exception\UnexpectedValueException;

trait MiddlewareValidatorTrait
{
    /**
     * Check if given input is a string, object or array.
     *
     * @param array|object|string $middleware
     *
     * @throws \Viserio\Component\Contract\Routing\Exception\UnexpectedValueException
     *
     * @return void
     */
    protected function validateInput($middleware): void
    {
        if (\is_array($middleware) || \is_string($middleware) || \is_object($middleware)) {
            return;
        }

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

    /**
     * Validates if given object or string has a middleware interface.
     *
     * @param \Psr\Http\Server\MiddlewareInterface|string $middleware
     *
     * @throws \Viserio\Component\Contract\Routing\Exception\UnexpectedValueException if \Psr\Http\Server\MiddlewareInterface was not found
     *
     * @return void
     */
    protected function validateMiddleware($middleware): void
    {
        $middleware = $this->getMiddlewareClassName($middleware);
        $interfaces = \class_implements($middleware);

        if (! isset($interfaces[MiddlewareInterface::class])) {
            throw new UnexpectedValueException(
                \sprintf('%s is not implemented in [%s].', MiddlewareInterface::class, $middleware)
            );
        }
    }

    /**
     * If input is a object returns full class name else the given string input.
     *
     * @param \Psr\Http\Server\MiddlewareInterface|string $middleware
     *
     * @return string
     */
    protected function getMiddlewareClassName($middleware): string
    {
        return \is_object($middleware) ? \get_class($middleware) : $middleware;
    }
}


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

use Interop\Container\ServiceProviderInterface;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface as PsrLoggerInterface;
use Viserio\Component\Contract\OptionsResolver\ProvidesDefaultOptions as ProvidesDefaultOptionsContract;
use Viserio\Component\Contract\OptionsResolver\RequiresComponentConfig as RequiresComponentConfigContract;
use Viserio\Component\Contract\Parser\Loader as LoaderContract;
use Viserio\Component\Contract\Translation\MessageFormatter as MessageFormatterContract;
use Viserio\Component\Contract\Translation\TranslationManager as TranslationManagerContract;
use Viserio\Component\Contract\Translation\Translator as TranslatorContract;
use Viserio\Component\OptionsResolver\Traits\OptionsResolverTrait;
use Viserio\Component\Translation\Formatter\IntlMessageFormatter;
use Viserio\Component\Translation\TranslationManager;

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

    /**
     * {@inheritdoc}
     */
    public function getFactories(): array
    {
        return [
            MessageFormatterContract::class   => [self::class, 'createMessageFormatter'],
            TranslationManagerContract::class => [self::class, 'createTranslationManager'],
            TranslationManager::class         => static function (ContainerInterface $container) {
                return $container->get(TranslationManagerContract::class);
            },
            TranslatorContract::class => [self::class, 'createTranslator'],
            'translator'              => static function (ContainerInterface $container) {
                return $container->get(TranslatorContract::class);
            },
        ];
    }

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

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

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

    /**
     * Create a new IntlMessageFormatter instance.
     *
     * @param \Psr\Container\ContainerInterface $container
     *
     * @return \Viserio\Component\Translation\Formatter\IntlMessageFormatter
     */
    public static function createMessageFormatter(ContainerInterface $container): IntlMessageFormatter
    {
        return new IntlMessageFormatter();
    }

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

        $manager = new TranslationManager($container->get(MessageFormatterContract::class));

        if ($container->has(LoaderContract::class)) {
            $manager->setLoader($container->get(LoaderContract::class));
        }

        if (isset($options['locale'])) {
            $manager->setLocale($options['locale']);
        }

        if (isset($options['directories'])) {
            $manager->setDirectories($options['directories']);
        }

        if (isset($options['files'])) {
            foreach ((array) $options['files'] as $import) {
                $manager->import($import);
            }
        }

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

        return $manager;
    }

    /**
     * Create a new Translation instance.
     *
     * @param \Psr\Container\ContainerInterface $container
     *
     * @return \Viserio\Component\Contract\Translation\Translator
     */
    public static function createTranslator(ContainerInterface $container): TranslatorContract
    {
        return $container->get(TranslationManager::class)->getTranslator();
    }
}


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

use Viserio\Component\Contract\Parser\Exception\ParseException;
use Viserio\Component\Contract\Parser\Parser as ParserContract;

class IniParser implements ParserContract
{
    /**
     * {@inheritdoc}
     */
    public function parse(string $payload): array
    {
        \set_error_handler(static function ($severity, $message, $file, $line): void {
            throw new ParseException([
                'severity' => $severity,
                'message'  => $message,
                'file'     => $file,
                'line'     => $line,
            ]);
        });

        $ini = \parse_ini_string(\trim($payload), true, \INI_SCANNER_RAW);

        \restore_error_handler();

        if (! $ini) {
            throw new ParseException(['message' => 'No parsable content.']);
        }

        foreach ($ini as $key => $value) {
            $ini[$key] = self::normalize($value);
        }

        return $ini;
    }

    /**
     * Normalizes INI and other values.
     *
     * @param mixed $value
     *
     * @return null|array|bool|int|string
     */
    private static function normalize($value)
    {
        // Normalize array values
        if (\is_array($value)) {
            foreach ($value as &$subValue) {
                $subValue = self::normalize($subValue);
            }

            return $value;
        }

        // Don't normalize non-string value
        if (! \is_string($value)) {
            return $value;
        }

        // Normalize true boolean value
        if (self::compareValues($value, ['true', 'on', 'yes'])) {
            return true;
        }

        // Normalize false boolean value
        if (self::compareValues($value, ['false', 'off', 'no', 'none'])) {
            return false;
        }

        // Normalize null value
        if (self::compareValues($value, ['null'])) {
            return null;
        }

        // Normalize numeric value
        if (\is_numeric($value)) {
            $numericValue = $value + 0;

            if ((\is_int($numericValue) && (int) $value === $numericValue)
                || (\is_float($numericValue) && (float) $value === $numericValue)
            ) {
                $value = $numericValue;
            }
        }

        return $value;
    }

    /**
     * Case insensitively compares values.
     *
     * @param string $value
     * @param array  $comparisons
     *
     * @return bool
     */
    private static function compareValues(string $value, array $comparisons): bool
    {
        foreach ($comparisons as $comparison) {
            if (\strcasecmp($value, $comparison) === 0) {
                return true;
            }
        }

        return false;
    }
}


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

use Symfony\Component\VarExporter\VarExporter;
use Viserio\Component\Console\Command\AbstractCommand;
use Viserio\Component\Contract\Config\Repository as RepositoryContract;

class ConfigCacheCommand extends AbstractCommand
{
    /**
     * {@inheritdoc}
     */
    protected static $defaultName = 'config:cache';

    /**
     * {@inheritdoc}
     */
    protected $signature = 'config:cache
        [dir : The config cache dir.]
    ';

    /**
     * {@inheritdoc}
     */
    protected $description = 'Create a cache file for faster configuration loading';

    /**
     * {@inheritdoc}
     */
    public function handle(): int
    {
        $this->callConfigClearCommand();

        $returnValue = \file_put_contents(
            $this->getCachedConfigPath(),
            '<?php' . \PHP_EOL . 'declare(strict_types=1);' . \PHP_EOL . \PHP_EOL . 'return ' . VarExporter::export($this->getConfiguration()) . ';' . \PHP_EOL
        );

        $this->info('Configuration cached successfully!');

        return (int) $returnValue;
    }

    /**
     * Call the config clear command.
     *
     * @return void
     */
    protected function callConfigClearCommand(): void
    {
        $this->call('config:clear', ['dir' => $this->argument('dir')]);
    }

    /**
     * Get the cached config file path.
     *
     * @return string
     */
    protected function getCachedConfigPath(): string
    {
        return $this->argument('dir') . \DIRECTORY_SEPARATOR . 'config.cache.php';
    }

    /**
     * Get all configuration.
     *
     * @return array
     */
    protected function getConfiguration(): array
    {
        return $this->getContainer()->get(RepositoryContract::class)->getAllProcessed();
    }
}


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

use Interop\Container\ServiceProviderInterface;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Container\ContainerInterface;
use Viserio\Component\Contract\Cron\Schedule as ScheduleContract;
use Viserio\Component\Contract\OptionsResolver\RequiresComponentConfig as RequiresComponentConfigContract;
use Viserio\Component\Contract\OptionsResolver\RequiresMandatoryOptions as RequiresMandatoryOptionsContract;
use Viserio\Component\Cron\Schedule;
use Viserio\Component\OptionsResolver\Traits\OptionsResolverTrait;

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

    /**
     * {@inheritdoc}
     */
    public function getFactories(): array
    {
        return [
            ScheduleContract::class => [self::class, 'createSchedule'],
            Schedule::class         => static function (ContainerInterface $container) {
                return $container->get(ScheduleContract::class);
            },
        ];
    }

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

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

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

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

        $scheduler = new Schedule($options['path'], $options['console']);

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

        $scheduler->setContainer($container);

        return $scheduler;
    }
}


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

class CompoundMatcher extends AbstractMatcher
{
    /**
     * A array of all SegmentMatcher.
     *
     * @var \Viserio\Component\Contract\Routing\SegmentMatcher[]
     */
    protected $matchers;

    /**
     * Create a new compound matcher instance.
     *
     * @param array $matchers
     */
    public function __construct(array $matchers)
    {
        $parameterKeys = [];

        foreach ($matchers as $matcher) {
            $parameterKeys = \array_merge($parameterKeys, $matcher->getParameterKeys());
        }

        $this->parameterKeys = $parameterKeys;
        $this->matchers      = $matchers;
    }

    /**
     * {@inheritdoc}
     */
    public function getConditionExpression(string $segmentVariable, int $uniqueKey = null): string
    {
        $conditions = [];

        foreach ($this->matchers as $key => $matcher) {
            $conditions[] = $matcher->getConditionExpression($segmentVariable, $uniqueKey + $key);
        }

        return \implode(' && ', $conditions);
    }

    /**
     * {@inheritdoc}
     */
    public function getMatchedParameterExpressions(string $segmentVariable, int $uniqueKey = null): array
    {
        $expressions = [];

        foreach ($this->matchers as $key => $matcher) {
            $matchedParameterExpressions = $matcher->getMatchedParameterExpressions(
                $segmentVariable,
                $uniqueKey + $key
            );

            foreach ($matchedParameterExpressions as $parameter => $expression) {
                $expressions[$parameter] = $expression;
            }
        }

        return $expressions;
    }

    /**
     * {@inheritdoc}
     */
    protected function getMatchHash(): string
    {
        $hashes = [];

        foreach ($this->matchers as $matcher) {
            $hashes[] = $matcher->getHash();
        }

        return \implode('::', $hashes);
    }
}


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

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Twig\Environment;
use Twig\Markup;
use Twig\Profiler\Dumper\HtmlDumper;
use Twig\Profiler\Profile;
use Viserio\Component\Contract\Profiler\AssetAware as AssetAwareContract;
use Viserio\Component\Contract\Profiler\PanelAware as PanelAwareContract;
use Viserio\Component\Contract\Profiler\TooltipAware as TooltipAwareContract;
use Viserio\Component\Profiler\DataCollector\AbstractDataCollector;

class TwigDataCollector extends AbstractDataCollector implements
    PanelAwareContract,
    AssetAwareContract,
    TooltipAwareContract
{
    /**
     * Twig profiler profile.
     *
     * @var \Twig\Profiler\Profile
     */
    private $profile;

    /**
     * Twig environment.
     *
     * @var \Twig\Environment
     */
    private $twigEnvironment;

    /**
     * Computed data.
     *
     * @var array
     */
    private $computed = [];

    /**
     * Create new twig collector instance.
     *
     * @param \Twig\Profiler\Profile $profile
     * @param \Twig\Environment      $twigEnvironment
     */
    public function __construct(Profile $profile, Environment $twigEnvironment)
    {
        $this->profile         = $profile;
        $this->twigEnvironment = $twigEnvironment;
    }

    /**
     * Get twig profile.
     *
     * @return \Twig\Profiler\Profile
     */
    public function getProfile(): Profile
    {
        return $this->profile;
    }

    /**
     * {@inheritdoc}
     */
    public function collect(ServerRequestInterface $serverRequest, ResponseInterface $response): void
    {
        $this->data['template_paths'] = [];

        $templateFinder = function (Profile $profile) use (&$templateFinder): void {
            if ($profile->isTemplate() &&
                $template = $this->twigEnvironment->load($profile->getName())->getSourceContext()->getPath()
            ) {
                $this->data['template_paths'][$profile->getName()] = $template;
            }

            foreach ($profile as $p) {
                $templateFinder($p);
            }
        };

        $templateFinder($this->profile);
    }

    /**
     * Get duration time.
     *
     * @return float
     */
    public function getTime(): float
    {
        return $this->getProfile()->getDuration() * 1000;
    }

    /**
     * Get counted templates.
     *
     * @return int
     */
    public function getTemplateCount(): int
    {
        return (int) $this->getComputedData('template_count');
    }

    /**
     * Get counted templates.
     *
     * @return array
     */
    public function getTemplates(): array
    {
        return $this->getComputedData('templates');
    }

    /**
     * Get counted blocks.
     *
     * @return int
     */
    public function getBlockCount(): int
    {
        return (int) $this->getComputedData('block_count');
    }

    /**
     * Get counted macros.
     *
     * @return int
     */
    public function getMacroCount(): int
    {
        return (int) $this->getComputedData('macro_count');
    }

    /**
     * List of twig file paths.
     *
     * @return array
     */
    public function getTemplatePaths(): array
    {
        return $this->data['template_paths'];
    }

    /**
     * Get a html call graph.
     *
     * @return \Twig\Markup
     *
     * @codeCoverageIgnore
     */
    public function getHtmlCallGraph(): Markup
    {
        $dumper = new HtmlDumper();
        $dump   = $dumper->dump($this->getProfile());

        // needed to remove the hardcoded CSS styles
        $dump = \str_replace([
            '',
            '',
            '',
        ], [
            '',
            '',
            '',
        ], $dump);

        return new Markup($dump, 'UTF-8');
    }

    /**
     * {@inheritdoc}
     *
     * @codeCoverageIgnore
     */
    public function getAssets(): array
    {
        return [
            'css' => \dirname(__DIR__) . \DIRECTORY_SEPARATOR . 'Resource' . \DIRECTORY_SEPARATOR . 'css' . \DIRECTORY_SEPARATOR . 'twig.css',
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function getPanel(): string
    {
        $data     = [];
        $twigHtml = $this->createMetrics(
            [
                'Render time'    => $this->formatDuration($this->getTime()),
                'Template calls' => $this->getTemplateCount(),
                'Block calls'    => $this->getBlockCount(),
                'Macro calls'    => $this->getMacroCount(),
            ],
            'Twig Metrics'
        );

        $templates = [];

        foreach ($this->getTemplates() as $template => $count) {
            $templates[$template] = $count;
        }

        $twigHtml .= $this->createTable(
            $templates,
            [
                'name'      => 'Rendered Templates',
                'headers'   => ['Template Name', 'Render Count'],
                'vardumper' => false,
            ]
        );

        $twigHtml .= '

Rendering Call Graph
';
        $twigHtml .= $this->getHtmlCallGraph();
        $twigHtml .= '
';
        $extensions = $this->twigEnvironment->getExtensions();
        $data[]     = ['name' => 'Twig ' . $this->getTemplateCount() . '', 'content' => $twigHtml];
        $data[]     = ['name' => 'Twig Extensions ' . \count($extensions) . '', 'content' => $this->createTable(
            \array_keys($extensions),
            ['headers' => ['Extension'], 'vardumper' => false]
        )];

        return $this->createTabs($data);
    }

    /**
     * {@inheritdoc}
     */
    public function getMenu(): array
    {
        return [
            'icon'  => \file_get_contents(\dirname(__DIR__) . \DIRECTORY_SEPARATOR . 'Resource' . \DIRECTORY_SEPARATOR . 'icons' . \DIRECTORY_SEPARATOR . 'ic_view_quilt_white_24px.svg'),
            'label' => 'Twig',
            'value' => '',
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function getTooltip(): string
    {
        return $this->createTooltipGroup([
            'Template calls' => $this->getComputedData('template_count'),
            'Block calls'    => $this->getComputedData('block_count'),
            'Macro calls'    => $this->getComputedData('macro_count'),
        ]);
    }

    /**
     * {@inheritdoc}
     */
    public function reset(): void
    {
        $this->profile->reset();

        $this->computed = [];
        $this->data     = [];
    }

    /**
     * Get computed data.
     *
     * @param string $index
     *
     * @return mixed
     */
    private function getComputedData(string $index)
    {
        if (\count($this->computed) === 0) {
            $this->computed = $this->generateComputeData($this->getProfile());
        }

        return $this->computed[$index];
    }

    /**
     * Generate Compute data.
     *
     * @param \Twig\Profiler\Profile $profile
     *
     * @return array
     */
    private function generateComputeData(Profile $profile): array
    {
        $data = [
            'template_count' => 0,
            'block_count'    => 0,
            'macro_count'    => 0,
        ];

        $templates = [];

        foreach ($profile as $p) {
            $d = $this->generateComputeData($p);

            $data['template_count'] += ($p->isTemplate() ? 1 : 0) + $d['template_count'];
            $data['block_count']    += ($p->isBlock() ? 1 : 0)    + $d['block_count'];
            $data['macro_count']    += ($p->isMacro() ? 1 : 0)    + $d['macro_count'];

            if ($p->isTemplate()) {
                if (! isset($templates[$p->getTemplate()])) {
                    $templates[$p->getTemplate()] = 1;
                } else {
                    $templates[$p->getTemplate()]++;
                }
            }

            foreach ($d['templates'] as $template => $count) {
                if (! isset($templates[$template])) {
                    $templates[$template] = $count;
                } else {
                    $templates[$template] += $count;
                }
            }
        }

        $data['templates'] = $templates;

        return $data;
    }
}


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

use Swift_DependencyContainer;
use Swift_Mailer;
use Swift_Transport;
use Viserio\Component\Contract\Events\Traits\EventManagerAwareTrait;
use Viserio\Component\Contract\Mail\Mailer as MailerContract;
use Viserio\Component\Contract\Manager\Exception\InvalidArgumentException;
use Viserio\Component\Contract\OptionsResolver\ProvidesDefaultOptions as ProvidesDefaultOptionsContract;
use Viserio\Component\Contract\Queue\QueueConnector as QueueContract;
use Viserio\Component\Contract\View\Traits\ViewAwareTrait;
use Viserio\Component\Manager\AbstractConnectionManager;
use Viserio\Component\Support\Str;

class MailManager extends AbstractConnectionManager implements ProvidesDefaultOptionsContract
{
    use EventManagerAwareTrait;
    use ViewAwareTrait;

    /**
     * A TransportFactory instance.
     *
     * @var \Viserio\Component\Mail\TransportFactory
     */
    private $transportFactory;

    /**
     * @var \Viserio\Component\Contract\Queue\QueueConnector
     */
    private $queueManager;

    /**
     * Create a new mail manager instance.
     *
     * @param array|\ArrayAccess                       $config
     * @param \Viserio\Component\Mail\TransportFactory $transportFactory
     */
    public function __construct($config, TransportFactory $transportFactory)
    {
        parent::__construct($config);

        $this->transportFactory = $transportFactory;
    }

    /**
     * Set the queue manager.
     *
     * @param \Viserio\Component\Contract\Queue\QueueConnector $queueManager
     *
     * @return void
     */
    public function setQueueManager(QueueContract $queueManager): void
    {
        $this->queueManager = $queueManager;
    }

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

    /**
     * {@inheritdoc}
     *
     * @throws \Viserio\Component\Contract\Manager\Exception\InvalidArgumentException
     *
     * @return \Viserio\Component\Contract\Mail\Mailer|\Viserio\Component\Contract\Mail\QueueMailer
     */
    protected function create(array $config, string $method, string $errorMessage): MailerContract
    {
        if (isset($this->extensions[$config['name']])) {
            return $this->callCustomCreator($config['name'], $config);
        }

        if ($this->transportFactory->hasTransport($config['driver'])) {
            return $this->createMailer(
                $this->transportFactory->getTransport(
                    $config['driver'],
                    $config['transporter'] ?? []
                ),
                $config
            );
        }

        throw new InvalidArgumentException(\sprintf('Mailer [%s] is not supported.', $config['name']));
    }

    /**
     * {@inheritdoc}
     */
    protected function getConfigFromName(string $name): array
    {
        $config = parent::getConfigFromName($name);

        $config['driver'] = $config['driver'] ?? $config['name'];

        return $config;
    }

    /**
     * {@inheritdoc}
     */
    protected static function getConfigName(): string
    {
        return 'mail';
    }

    /**
     * Create a new SwiftMailer instance.
     *
     * @param \Swift_Transport $transport
     *
     * @return \Swift_Mailer
     */
    protected function createSwiftMailer(Swift_Transport $transport): Swift_Mailer
    {
        if (isset($this->resolvedOptions['domain'])) {
            Swift_DependencyContainer::getInstance()
                ->register('mime.idgenerator.idright')
                ->asValue($this->resolvedOptions['domain']);
        }

        return new Swift_Mailer($transport);
    }

    /**
     * Create a mailer or queue mailer instance.
     *
     * @param \Swift_Transport $transport
     * @param array            $config
     *
     * @return \Viserio\Component\Contract\Mail\Mailer|\Viserio\Component\Contract\Mail\QueueMailer
     */
    private function createMailer(Swift_Transport $transport, array $config): MailerContract
    {
        $swiftMailer = $this->createSwiftMailer($transport);

        if ($this->queueManager !== null) {
            $mailer = new QueueMailer($swiftMailer, $this->queueManager, $config);
        } else {
            $mailer = new Mailer($swiftMailer, $config);
        }

        if ($this->container !== null) {
            $mailer->setContainer($this->container);
        }

        if ($this->viewFactory !== null) {
            $mailer->setViewFactory($this->viewFactory);
        }

        if ($this->eventManager !== null) {
            $mailer->setEventManager($this->eventManager);
        }

        // Next we will set all of the global addresses on this mailer, which allows
        // for easy unification of all "from" addresses as well as easy debugging
        // of sent messages since they get be sent into a single email address.
        foreach (['from', 'reply_to', 'to'] as $type) {
            $this->setGlobalAddress($mailer, $type);
        }

        return $mailer;
    }

    /**
     * Set a global address on the mailer by type.
     *
     * @param \Viserio\Component\Contract\Mail\Mailer $mailer
     * @param string                                  $type
     *
     * @return void
     */
    private function setGlobalAddress(MailerContract $mailer, string $type): void
    {
        if (! isset($this->resolvedOptions[$type])) {
            return;
        }

        $address = $this->resolvedOptions[$type];

        if (\is_array($address) && isset($address['address'])) {
            $mailer->{'always' . Str::studly($type)}($address['address'], $address['name']);
        }
    }
}


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

use Symfony\Component\VarDumper\Caster\Caster;
use Symfony\Component\VarDumper\Caster\ClassStub;
use Symfony\Component\VarDumper\Caster\LinkStub;
use Symfony\Component\VarDumper\Caster\StubCaster;
use Symfony\Component\VarDumper\Cloner\AbstractCloner;
use Symfony\Component\VarDumper\Cloner\Stub;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Viserio\Component\Contract\Profiler\DataCollector as DataCollectorContract;
use Viserio\Component\Profiler\Util\HtmlDumperOutput;
use Viserio\Component\Support\Debug\HtmlDumper;
use Viserio\Component\Support\Str;
use Viserio\Component\Support\Traits\BytesFormatTrait;

abstract class AbstractDataCollector implements DataCollectorContract
{
    use BytesFormatTrait;

    /**
     * Array of all collected datas.
     *
     * @var array
     */
    protected $data;

    /**
     * Configured VarCloner instance.
     *
     * @var \Symfony\Component\VarDumper\Cloner\AbstractCloner
     */
    private static $cloner;

    /**
     * HtmlDumper instance.
     *
     * @var \Viserio\Component\Support\Debug\HtmlDumper
     */
    private static $htmlDumper;

    /**
     * HtmlDumperOutput instance.
     *
     * @var \Viserio\Component\Profiler\Util\HtmlDumperOutput
     */
    private static $htmlDumperOutput;

    /**
     * The cache of stubs.
     *
     * @var array
     */
    private static $stubsCache = [];

    /**
     * Get all collected data.
     *
     * @return array
     */
    public function getData(): array
    {
        return $this->data;
    }

    /**
     * {@inheritdoc}
     */
    public function getMenuPosition(): string
    {
        return 'left';
    }

    /**
     * {@inheritdoc}
     */
    public function getName(): string
    {
        $namespace = \mb_substr(static::class, 0, \mb_strrpos(static::class, '\\'));

        return Str::snake(\str_replace($namespace . '\\', '', \get_class($this)), '-');
    }

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

    /**
     * Add measurement to float time.
     *
     * @param float $seconds
     *
     * @return string
     *
     * @codeCoverageIgnore
     */
    protected function formatDuration(float $seconds): string
    {
        if ($seconds < 0.001) {
            return \round($seconds * 1000000) . 'μs';
        }

        if ($seconds < 1) {
            return \round($seconds * 1000, 2) . 'ms';
        }

        return \round($seconds, 2) . 's';
    }

    /**
     * Creates a tooltip group from array.
     *
     * array[]
     *     ['key']       string          Defines the name of  html element.
     *     ['value']     array|string    Defines the content to be shown in the  html element.
     *         ['class'] string          Adds a class to the  html element.
     *         ['value'] string          Adds the content.
     *
     * @param array $data (See above)
     *
     * @return string
     */
    protected function createTooltipGroup(array $data): string
    {
        $tooltip = '
';

        foreach ($data as $strong => $infos) {
            $tooltip .= '
';

            if (\is_array($infos)) {
                $tooltip .= '' . $strong . '';

                foreach ($infos as $info) {
                    $class = isset($info['class']) ? ' class="' . $info['class'] . '"' : '';

                    $tooltip .= '' . $info['value'] . '';
                }
            } elseif (\is_int($strong) && \is_string($infos)) {
                $tooltip .= $infos;
            } else {
                $tooltip .= '' . $strong . '' . $infos . '';
            }

            $tooltip .= '
';
        }

        $tooltip .= '
';

        return $tooltip;
    }

    /**
     * Creates a tab slider.
     *
     * array[]         array     Defines witch tabs a shown.
     *     ['name']    string    Name of the tab
     *     ['content'] string    Tab content.
     *
     * @param array $data (See above)
     *
     * @return string
     */
    protected function createTabs(array $data): string
    {
        $html = '
';

        foreach ($data as $key => $value) {
            $id = \uniqid($key . '-', true);

            $html .= '
';
            $html .= '<input type="radio" name="tabgroup" id="tab-' . $id . '">';
            $html .= '' . $value['name'] . '';
            $html .= '
';
            $html .= $value['content'];
            $html .= '

';
        }

        $html .= '
';

        return $html;
    }

    /**
     * Creates a table.
     *
     * @param array $data
     * @param array $settings
     *
     * @return string
     */
    protected function createTable(array $data, array $settings = []): string
    {
        $options = \array_merge([
            'name'       => null,
            'headers'    => ['Key', 'Value'],
            'vardumper'  => true,
            'empty_text' => 'Empty',
        ], $settings);

        $html = $options['name'] !== null ? '
' . $options['name'] . '
' : '';

        if (\count($data) !== 0) {
            $html .= '

';

            foreach ((array) $options['headers'] as $header) {
                $html .= '' . $header . '	';
            }

            $html .= '

';

            foreach ($data as $key => $values) {
                if (\is_string($key)) {
                    $html .= '
';
                    $html .= '' . $key . '	';
                    $html .= \sprintf('%s	', ($options['vardumper'] ? $this->cloneVar($values) : $values));
                    $html .= '
';
                } else {
                    $html .= '
';

                    if (\is_array($values)) {
                        foreach ($values as $k => $value) {
                            $html .= \sprintf('%s	', ($options['vardumper'] ? $this->cloneVar($value) : $value));
                        }
                    } else {
                        $html .= \sprintf('%s	', ($options['vardumper'] ? $this->cloneVar($values) : $values));
                    }

                    $html .= '
';
                }
            }

            $html .= '

';
        } else {
            $html .= \sprintf('
%s
', $options['empty_text']);
        }

        return $html;
    }

    /**
     * Create a dropdown menu content.
     *
     * @param array $data
     *
     * @return string
     */
    protected function createDropdownMenuContent(array $data): string
    {
        $selects  = $content  = [];
        $selected = false;

        foreach ($data as $key => $value) {
            $id = 'content-' . $key . '-' . \uniqid('', true);

            $selected = $selected === false ? $selected = 'selected' : '';

            $selects[$key] = '<option value="' . $id . '"' . $selected . '>' . $key . '</option>';
            $content[$key] = '
' . $value . '
';
        }

        $html = '<select class="content-selector" name="' . $this->getName() . '">';

        foreach ($selects as $key => $value) {
            $html .= $value;
        }

        $html .= '</select>';

        foreach ($content as $key => $value) {
            $html .= $value;
        }

        return $html;
    }

    /**
     * Create a metrics.
     *
     * @param array       $data
     * @param null|string $name
     *
     * @return string
     */
    protected function createMetrics(array $data, ?string $name = null): string
    {
        $html = $name !== null ? '
' . $name . '
' : '';
        $html .= '
';

        foreach ($data as $key => $value) {
            $html .= '
';
            $html .= '' . $value . '' . $key . '';
            $html .= '
';
        }

        $html .= '
';

        return $html;
    }

    /**
     * Converts the variable into a serializable Data instance.
     *
     * @param mixed $var
     *
     * @return null|string
     */
    protected function cloneVar($var): ?string
    {
        $cloneVar = self::getCloner()->cloneVar($this->decorateVar($var), Caster::EXCLUDE_VERBOSE);
        $dumper   = self::getDumper();

        $dumper->dump(
            $cloneVar,
            self::$htmlDumperOutput
        );

        $output = self::$htmlDumperOutput->getOutput();
        self::$htmlDumperOutput->reset();

        return $output;
    }

    /**
     * Get the cloner used for dumping variables.
     *
     * @return \Symfony\Component\VarDumper\Cloner\AbstractCloner
     */
    private static function getCloner(): AbstractCloner
    {
        if (self::$cloner === null) {
            self::$cloner = new VarCloner();
            self::$cloner->setMaxItems(250);
            self::$cloner->addCasters([
                Stub::class => static function (Stub $v, array $a, Stub $s, $isNested) {
                    return $isNested ? $a : StubCaster::castStub($v, $a, $s, true);
                },
            ]);
        }

        return self::$cloner;
    }

    /**
     * Get a HtmlDumper instance.
     *
     * @return \Viserio\Component\Support\Debug\HtmlDumper
     */
    private static function getDumper(): HtmlDumper
    {
        if (self::$htmlDumper === null) {
            self::$htmlDumperOutput = new HtmlDumperOutput();

            // re-use the same var-dumper instance, so it won't re-render the global styles/scripts on each dump.
            self::$htmlDumper = new HtmlDumper(self::$htmlDumperOutput);
        }

        return self::$htmlDumper;
    }

    /**
     * @param mixed $var
     *
     * @return mixed
     */
    private function decorateVar($var)
    {
        if (\is_array($var)) {
            if (isset($var[0], $var[1]) && \is_callable($var)) {
                return ClassStub::wrapCallable($var);
            }

            foreach ($var as $k => $v) {
                if ($v !== $d = $this->decorateVar($v)) {
                    $var[$k] = $d;
                }
            }

            return $var;
        }

        if (\is_string($var)) {
            if (isset(self::$stubsCache[$var])) {
                return self::$stubsCache[$var];
            }

            if (\mb_strpos($var, '\\') !== false) {
                $i = \mb_strpos($var, '::');
                $c = ($i !== false) ? \mb_substr($var, 0, $i) : $var;

                if (\class_exists($c, false) || \interface_exists($c, false) || \trait_exists($c, false)) {
                    return self::$stubsCache[$var] = new ClassStub($var);
                }
            }

            if (\mb_strpos($var, \DIRECTORY_SEPARATOR) !== false &&
                \mb_strpos($var, '://') !== false &&
                \mb_strpos($var, "\0") && @\is_file($var) === false
            ) {
                return self::$stubsCache[$var] = new LinkStub($var);
            }
        }

        return $var;
    }
}


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

use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Process;
use Viserio\Component\Console\Command\AbstractCommand;
use Viserio\Component\WebServer\Command\Traits\ServerCommandRequirementsCheckTrait;
use Viserio\Component\WebServer\WebServer;
use Viserio\Component\WebServer\WebServerConfig;

final class ServerServeCommand extends AbstractCommand
{
    use ServerCommandRequirementsCheckTrait;

    /**
     * {@inheritdoc}
     */
    protected static $defaultName = 'server:serve';

    /**
     * {@inheritdoc}
     */
    protected $signature = 'server:serve
        [-H|--host= : The hostname to listen to.]
        [-p|--port= : The port to listen to.]
        [-r|--router= : Path to custom router script.]
        [--pidfile= : PID file.]
        [--disable-xdebug : Disable xdebug on server]
    ';

    /**
     * {@inheritdoc}
     */
    protected $description = 'Runs a local web server.';

    /**
     * Create a new ServerServeCommand Instance.
     *
     * @param string $documentRoot
     * @param string $environment
     */
    public function __construct(string $documentRoot, string $environment)
    {
        $this->documentRoot = $documentRoot;
        $this->environment  = $environment;

        parent::__construct();
    }

    /**
     * {@inheritdoc}
     */
    public function handle(): int
    {
        if ($this->checkRequirements() === 1) {
            return 1;
        }

        $webServerConfig = new WebServerConfig($this->documentRoot, $this->environment, $this);

        $callback      = null;
        $disableOutput = false;
        $output        = $this->getOutput();

        if ($output->isQuiet()) {
            $disableOutput = true;
        } else {
            $callback = static function ($type, $buffer) use ($output): void {
                if (Process::ERR === $type && $output instanceof ConsoleOutputInterface) {
                    $output = $output->getErrorOutput();
                }

                $output->write($buffer, false, OutputInterface::OUTPUT_RAW);
            };
        }

        try {
            $host            = $webServerConfig->getHostname();
            $port            = $webServerConfig->getPort();
            $resolvedAddress = $webServerConfig->getDisplayAddress();

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

            if ($webServerConfig->hasXdebug()) {
                $output->comment('Xdebug profiler trigger enabled.');
            }

            WebServer::run($webServerConfig, $disableOutput, $callback);
        } catch (\Exception $exception) {
            $this->error($exception->getMessage());

            return 1;
        }

        return 0;
    }
}


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

class ComposerExtraProcessor extends AbstractParameterProcessor
{
    /**
     * Path to the composer.json.
     *
     * @var string
     */
    private $composerJsonPath;

    /**
     * Create a new ComposerExtraProcessor instance.
     *
     * @param string $composerJsonPath
     */
    public function __construct(string $composerJsonPath)
    {
        $this->composerJsonPath = $composerJsonPath;
    }

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

    /**
     * {@inheritdoc}
     */
    public function process(string $data)
    {
        $json = \json_decode(\trim(\file_get_contents($this->composerJsonPath)), true);

        if (\json_last_error() !== \JSON_ERROR_NONE) {
            throw new \RuntimeException(\sprintf('%s in [%s] file.', \json_last_error_msg(), $this->composerJsonPath), \json_last_error());
        }

        $parameterKey = $this->parseParameter($data);

        $newValue = $json['extra'][$parameterKey] ?? null;

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

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


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

final class PHPCodeCollection
{
    /**
     * The php code.
     *
     * @var string
     */
    public $code = '';

    /**
     * The current indentation level of the code.
     *
     * @var int
     */
    public $indent = '';

    /**
     * Appends the supplied code to the builder.
     *
     * @param string $code
     */
    public function append(string $code): void
    {
        $indent = \str_repeat(' ', 4 * $this->indent);

        $this->code .= $indent . \str_replace(\PHP_EOL, \PHP_EOL . $indent, $code);
    }

    /**
     * Appends the supplied code and a new line to the builder.
     *
     * @param string $code
     */
    public function appendLine(string $code = ''): void
    {
        $this->append($code);
        $this->code .= \PHP_EOL;
    }
}


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

use Twig\Environment;
use Twig\Extension\ExtensionInterface;
use Viserio\Component\Contract\Container\Traits\ContainerAwareTrait;
use Viserio\Component\Contract\OptionsResolver\ProvidesDefaultOptions as ProvidesDefaultOptionsContract;
use Viserio\Component\Contract\View\Exception\RuntimeException;
use Viserio\Component\View\Engine\AbstractBaseEngine;

class TwigEngine extends AbstractBaseEngine implements ProvidesDefaultOptionsContract
{
    use ContainerAwareTrait;

    /**
     * Twig environment.
     *
     * @var \Twig\Environment
     */
    protected $twig;

    /**
     * Create a new engine instance.
     *
     * @param \Twig\Environment  $twig
     * @param array|\ArrayAccess $config
     */
    public function __construct(Environment $twig, $config)
    {
        $this->resolvedOptions = self::resolveOptions($config);
        $this->twig            = $twig;
    }

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

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

    /**
     * {@inheritdoc}
     *
     * @throws \Twig_Error_Loader
     * @throws \Twig_Error_Runtime
     * @throws \Twig_Error_Syntax
     * @throws \Viserio\Component\Contract\View\Exception\RuntimeException
     */
    public function get(array $fileInfo, array $data = []): string
    {
        $twig = $this->addExtensions($this->twig, $this->resolvedOptions['engines']['twig']);

        return $twig->render($fileInfo['name'] ?? '', $data);
    }

    /**
     * Add extensions to twig environment.
     *
     * @param \Twig\Environment $twig
     * @param array             $config
     *
     * @throws \Viserio\Component\Contract\View\Exception\RuntimeException
     *
     * @return \Twig\Environment
     */
    protected function addExtensions(Environment $twig, array $config): Environment
    {
        if (isset($config['extensions']) && \is_array($config['extensions'])) {
            foreach ($config['extensions'] as $extension) {
                if ($this->container !== null && \is_string($extension) && $this->container->has($extension)) {
                    $twig->addExtension($this->container->get($extension));
                } elseif (\is_object($extension) && $extension instanceof ExtensionInterface) {
                    $twig->addExtension($extension);
                } else {
                    throw new RuntimeException(\sprintf(
                        'Twig extension [%s] is not a object.',
                        $extension
                    ));
                }
            }
        }

        return $twig;
    }
}


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

use Interop\Container\ServiceProviderInterface;
use Psr\Container\ContainerInterface;
use Viserio\Component\Contract\Translation\Translator as TranslatorContract;
use Viserio\Component\Contract\Validation\Validator as ValidatorContract;
use Viserio\Component\Validation\Validator;

class ValidationServiceProvider implements ServiceProviderInterface
{
    /**
     * {@inheritdoc}
     */
    public function getFactories(): array
    {
        return [
            ValidatorContract::class => [self::class, 'createValidator'],
            Validator::class         => static function (ContainerInterface $container) {
                return $container->get(ValidatorContract::class);
            },
            'validator' => static function (ContainerInterface $container) {
                return $container->get(ValidatorContract::class);
            },
        ];
    }

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

    /**
     * Create a validator instance.
     *
     * @param \Psr\Container\ContainerInterface $container
     *
     * @return \Viserio\Component\Contract\Validation\Validator
     */
    public static function createValidator(ContainerInterface $container): ValidatorContract
    {
        $validator = new Validator();

        // @codeCoverageIgnoreStart
        if ($container->has(TranslatorContract::class)) {
            $validator->setTranslator($container->get(TranslatorContract::class));
        }
        // @codeCoverageIgnoreEnd

        return $validator;
    }
}


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

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

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
    {
        $commands = [];

        if (\class_exists(BaseConfigCacheCommand::class)) {
            $commands = \array_merge(
                $commands,
                [
                    'config:cache' => ConfigCacheCommand::class,
                    'config:clear' => ConfigClearCommand::class,
                ]
            );
        }

        return [
            'lazily_commands' => $commands,
        ];
    }

    /**
     * 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) {
            $commands = [];

            if (\class_exists(BaseConfigCacheCommand::class)) {
                $commands = \array_merge(
                    $commands,
                    [
                        new ConfigCacheCommand(),
                        new ConfigClearCommand(),
                    ]
                );
            }

            $console->addCommands($commands);
        }

        return $console;
    }
}


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

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Viserio\Component\Contract\Profiler\TooltipAware as TooltipAwareContract;
use Viserio\Component\Foundation\AbstractKernel;
use Viserio\Component\Profiler\DataCollector\PhpInfoDataCollector;

class NarrowsparkDataCollector extends PhpInfoDataCollector implements TooltipAwareContract
{
    /**
     * A server request instance.
     *
     * @var \Psr\Http\Message\ServerRequestInterface
     */
    private $serverRequest;

    /**
     * Gets the environment.
     *
     * @var string
     */
    private $env;

    /**
     * Is debug enabled.
     *
     * @var bool
     */
    private $isDebug;

    /**
     * Create a new narrowspark collector instance.
     *
     * @param string $env
     * @param bool   $isDebug
     */
    public function __construct($env = 'local', $isDebug = true)
    {
        $this->env     = $env;
        $this->isDebug = $isDebug;
    }

    /**
     * {@inheritdoc}
     */
    public function collect(ServerRequestInterface $serverRequest, ResponseInterface $response): void
    {
        parent::collect($serverRequest, $response);

        $this->serverRequest = $serverRequest;
    }

    /**
     * {@inheritdoc}
     */
    public function getName(): string
    {
        return 'narrowspark';
    }

    /**
     * {@inheritdoc}
     */
    public function getMenuPosition(): string
    {
        return 'right';
    }

    /**
     * {@inheritdoc}
     */
    public function getMenu(): array
    {
        return [
            'icon'  => 'ic_narrowspark_white_24px.svg',
            'label' => '',
            'value' => AbstractKernel::VERSION,
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function getTooltip(): string
    {
        $opcache = \extension_loaded('Zend OPcache') && \filter_var(\ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN);

        $tooltip = $this->createTooltipGroup([
            'Profiler token'   => $this->serverRequest->getHeaderLine('x-debug-token'),
            'Application name' => '',
            'Environment'      => $this->env,
            'Debug'            => [
                [
                    'class' => $this->isDebug !== false ? 'status-green' : 'status-red',
                    'value' => $this->isDebug !== false ? 'enabled' : 'disabled',
                ],
            ],
        ]);

        $tooltip .= $this->createTooltipGroup([
            'PHP version'    => \PHP_VERSION,
            'Architecture'   => \PHP_INT_SIZE * 8,
            'Timezone'       => \date_default_timezone_get(),
            'PHP Extensions' => [
                [
                    'class' => \extension_loaded('xdebug') ? 'status-green' : 'status-red',
                    'value' => 'Xdebug',
                ],
                [
                    'class' => $opcache ? 'status-green' : 'status-red',
                    'value' => 'OPcache',
                ],
            ],
            'PHP SAPI' => \PHP_SAPI,
        ]);

        $version = AbstractKernel::VERSION;

        $tooltip .= $this->createTooltipGroup([
            'Resource'  => 'Read Narrowspark Doc\'s ' . $version . '',
            'Help'      => 'Narrowspark Support Channels',
        ]);

        return $tooltip;
    }
}


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

use InvalidArgumentException;
use RuntimeException;
use Symfony\Component\Yaml\Yaml as SymfonyYaml;
use Viserio\Component\Contract\Parser\Dumper as DumperContract;
use Viserio\Component\Contract\Parser\Exception\DumpException;

class YamlDumper implements DumperContract
{
    /**
     * Create a new Yaml parser.
     *
     * @throws \RuntimeException
     */
    public function __construct()
    {
        // @codeCoverageIgnoreStart
        if (! \class_exists(SymfonyYaml::class)) {
            throw new RuntimeException('Unable to read yaml as the Symfony Yaml Component is not installed.');
        }
        // @codeCoverageIgnoreEnd
    }

    /**
     * {@inheritdoc}
     *
     * @codeCoverageIgnore
     */
    public function dump(array $data): string
    {
        try {
            return SymfonyYaml::dump($data);
        } catch (InvalidArgumentException $exception) {
            throw new DumpException($exception->getMessage());
        }
    }
}


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

use Symfony\Component\Console\Helper\Table;
use Viserio\Component\Console\Command\AbstractCommand;
use Viserio\Component\Cron\Cron;
use Viserio\Component\Cron\Schedule;

class CronListCommand extends AbstractCommand
{
    /**
     * {@inheritdoc}
     */
    protected static $defaultName = 'cron:list';

    /**
     * {@inheritdoc}
     */
    protected $description = 'List all Cron jobs';

    /**
     * {@inheritdoc}
     *
     * @param \Viserio\Component\Cron\Schedule $schedule
     */
    public function handle(Schedule $schedule): int
    {
        $cronJobs = $schedule->getCronJobs();

        $table = new Table($this->getOutput());
        $table->setHeaders(['Jobname', 'Expression', 'Summary']);

        $rows = [];

        /** @var Cron $cronJob */
        foreach ($cronJobs as $cronJob) {
            $rows[] = [
                $cronJob->getCommand(),
                $cronJob->getExpression(),
                $cronJob->getSummaryForDisplay(),
            ];
        }

        $table->setRows($rows);

        $table->render();

        return 0;
    }
}


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

use Cake\Chronos\Chronos;
use Interop\Container\ServiceProviderInterface;
use Narrowspark\HttpStatus\HttpStatus;
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\HttpFoundation\Console\Command\DownCommand;
use Viserio\Component\HttpFoundation\Console\Command\UpCommand;

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
    {
        $commands = [];

        if (\class_exists(Chronos::class) && \class_exists(HttpStatus::class)) {
            $commands = \array_merge(
                $commands,
                [
                    'app:down' => DownCommand::class,
                    'app:up'   => UpCommand::class,
                ]
            );
        }

        return [
            'lazily_commands' => $commands,
        ];
    }

    /**
     * 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) {
            $commands = [];

            if (\class_exists(Chronos::class) && \class_exists(HttpStatus::class)) {
                $commands = \array_merge(
                    $commands,
                    [
                        new DownCommand(),
                        new UpCommand(),
                    ]
                );
            }

            $console->addCommands($commands);
        }

        return $console;
    }
}


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

class SortedMiddleware
{
    /**
     * All middleware.
     *
     * @var array
     */
    protected $middleware = [];

    /**
     * Create a new Sorted Middleware container.
     *
     * @param array $priorityMap
     * @param array $middleware
     */
    public function __construct(array $priorityMap, array $middleware)
    {
        $this->middleware = $this->doSortMiddleware($priorityMap, $middleware);
    }

    /**
     * Get all sorted middleware.
     *
     * @return array
     */
    public function getAll(): array
    {
        return $this->middleware;
    }

    /**
     * Sort the middleware by the given priority map.
     *
     * Each call to this method makes one discrete middleware movement if necessary.
     *
     * @param array $priorityMap
     * @param array $middleware
     *
     * @return array
     */
    protected function doSortMiddleware(array $priorityMap, array $middleware): array
    {
        $lastIndex = $lastPriorityIndex = 0;

        foreach ($middleware as $index => $mware) {
            if (\in_array($mware, $priorityMap, true)) {
                $priorityIndex = \array_search($mware, $priorityMap, true);

                // This middleware is in the priority map. If we have encountered another middleware
                // that was also in the priority map and was at a lower priority than the current
                // middleware, we will move this middleware to be above the previous encounter.
                if (isset($lastPriorityIndex) && $priorityIndex < $lastPriorityIndex) {
                    return $this->doSortMiddleware(
                        $priorityMap,
                        \array_values(
                            $this->moveMiddleware($middleware, $index, $lastIndex)
                        )
                    );
                }

                // This middleware is in the priority map; but, this is the first middleware we have
                // encountered from the map thus far. We'll save its current index plus its index
                // from the priority map so we can compare against them on the next iterations.
                $lastIndex         = $index;
                $lastPriorityIndex = $priorityIndex;
            }
        }

        return \array_values(\array_unique($middleware, \SORT_REGULAR));
    }

    /**
     * Splice a middleware into a new position and remove the old entry.
     *
     * @param array $middleware
     * @param int   $from
     * @param int   $to
     *
     * @return array
     */
    protected function moveMiddleware(array $middleware, int $from, int $to): array
    {
        \array_splice($middleware, $to, 0, $middleware[$from]);

        unset($middleware[$from + 1]);

        return $middleware;
    }
}