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

use Psr\Log\LoggerInterface;
use Swift_Message;
use Swift_Mime_SimpleMessage;

class LogTransport extends AbstractTransport
{
    /**
     * The Logger instance.
     *
     * @var \Psr\Log\LoggerInterface
     */
    protected $logger;

    /**
     * Create a new log transport instance.
     *
     * @param \Psr\Log\LoggerInterface $logger
     */
    public function __construct(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }

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

        $this->logger->debug($this->getMimeEntityString($message));

        $this->sendPerformed($message);

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

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

    /**
     * Get a loggable string out of a Swiftmailer entity.
     *
     * @param \Swift_Message $entity
     *
     * @return string
     */
    protected function getMimeEntityString(Swift_Message $entity): string
    {
        $string = (string) $entity->getHeaders() . \PHP_EOL . $entity->getBody();

        foreach ($entity->getChildren() as $children) {
            $string .= \PHP_EOL . \PHP_EOL . $this->getMimeEntityString($children);
        }

        return $string;
    }
}


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

use Invoker\ParameterResolver\ParameterResolver;
use ReflectionFunctionAbstract;

/**
 * Code in this class it taken from silly.
 *
 * See the original here: https://github.com/mnapoli/silly/blob/master/src/HyphenatedInputResolver.php
 *
 * @author Matthieu Napoli https://github.com/mnapoli
 * @copyright Copyright (c) Matthieu Napoli
 */
class HyphenatedInputResolver implements ParameterResolver
{
    /**
     * Tries to maps hyphenated parameters to a similarly-named,
     * non-hyphenated parameters in the function signature.
     *
     * E.g. `->call($callable, ['dry-run' => true])` will inject the boolean `true`
     * for a parameter named either `$dryrun` or `$dryRun`.
     *
     * @param \ReflectionFunctionAbstract $reflection
     * @param array                       $providedParameters
     * @param array                       $resolvedParameters
     *
     * @return array
     */
    public function getParameters(
        ReflectionFunctionAbstract $reflection,
        array $providedParameters,
        array $resolvedParameters
    ): array {
        $parameters = [];

        foreach ($reflection->getParameters() as $index => $parameter) {
            $parameters[\mb_strtolower($parameter->name)] = $index;
        }

        foreach ($providedParameters as $name => $value) {
            $normalizedName = \mb_strtolower(\str_replace('-', '', $name));

            // Skip parameters that do not exist with the normalized name
            if (! \array_key_exists($normalizedName, $parameters)) {
                continue;
            }

            $normalizedParameterIndex = $parameters[$normalizedName];

            // Skip parameters already resolved
            if (\array_key_exists($normalizedParameterIndex, $resolvedParameters)) {
                continue;
            }

            $resolvedParameters[$normalizedParameterIndex] = $value;
        }

        return $resolvedParameters;
    }
}


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

use Error;

/**
 * The following is derived from code at http://github.com/nikic/PHP-Parser.
 *
 * Copyright (c) 2011-2018 by Nikita Popov.
 *
 * Some rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *    * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *
 *    * Redistributions in binary form must reproduce the above
 *      copyright notice, this list of conditions and the following
 *      disclaimer in the documentation and/or other materials provided
 *      with the distribution.
 *
 *    * The names of the contributors may not be used to endorse or
 *      promote products derived from this software without specific
 *      prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @internal
 */
final class ScalarString
{
    // For use in "kind" attribute
    public const KIND_SINGLE_QUOTED = 1;
    public const KIND_DOUBLE_QUOTED = 2;
    public const KIND_HEREDOC       = 3;
    public const KIND_NOWDOC        = 4;

    /**
     * @var array
     */
    private static $replacements = [
        '\\' => '\\',
        '$'  => '$',
        'n'  => "\n",
        'r'  => "\r",
        't'  => "\t",
        'f'  => "\f",
        'v'  => "\v",
        'e'  => "\x1B",
    ];

    /**
     * @internal
     *
     * Parses a string token
     *
     * @param string $str                String token content
     * @param bool   $parseUnicodeEscape Whether to parse PHP 7 \u escapes
     *
     * @return string The parsed string
     */
    public static function parse(string $str, bool $parseUnicodeEscape = true): string
    {
        $bLength = 0;

        if ('b' === $str[0] || 'B' === $str[0]) {
            $bLength = 1;
        }

        if ('\'' === $str[$bLength]) {
            return \str_replace(
                ['\\\\', '\\\''],
                ['\\', '\''],
                \mb_substr($str, $bLength + 1, -1)
            );
        }

        return self::parseEscapeSequences(
            \mb_substr($str, $bLength + 1, -1),
            '"',
            $parseUnicodeEscape
        );
    }

    /**
     * @internal
     *
     * Parses a constant doc string
     *
     * @param string $startToken         Doc string start token content (<<<SMTHG)
     * @param string $str                String token content
     * @param bool   $parseUnicodeEscape Whether to parse PHP 7 \u escapes
     *
     * @return string Parsed string
     */
    public static function parseDocString(string $startToken, string $str, bool $parseUnicodeEscape = true): string
    {
        // strip last newline (thanks tokenizer for sticking it into the string!)
        $str = \preg_replace('~(\r\n|\n|\r)\z~', '', $str);

        // nowdoc string
        if (false !== \mb_strpos($startToken, '\'')) {
            return $str;
        }

        return self::parseEscapeSequences($str, null, $parseUnicodeEscape);
    }

    /**
     * @internal
     *
     * Parses escape sequences in strings (all string types apart from single quoted)
     *
     * @param string      $str                String without quotes
     * @param null|string $quote              Quote type
     * @param bool        $parseUnicodeEscape Whether to parse PHP 7 \u escapes
     *
     * @return string String with escape sequences parsed
     */
    public static function parseEscapeSequences(string $str, $quote, bool $parseUnicodeEscape = true): string
    {
        if (null !== $quote) {
            $str = \str_replace('\\' . $quote, $quote, $str);
        }
        $extra = '';

        if ($parseUnicodeEscape) {
            $extra = '|u\{([0-9a-fA-F]+)\}';
        }

        return \preg_replace_callback(
            '~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3}' . $extra . ')~',
            static function ($matches) {
                $str = $matches[1];

                if (isset(self::$replacements[$str])) {
                    return self::$replacements[$str];
                }

                if ('x' === $str[0] || 'X' === $str[0]) {
                    return \chr((int) \hexdec($str));
                }

                if ('u' === $str[0]) {
                    // @codeCoverageIgnoreStart
                    return self::codePointToUtf8((int) \hexdec($matches[2]));
                    // @codeCoverageIgnoreEnd
                }

                return \chr(\octdec($str));
            },
            $str
        );
    }

    /**
     * Converts a Unicode code point to its UTF-8 encoded representation.
     *
     * @param int $num Code point
     *
     * @return string UTF-8 representation of code point
     *
     * @codeCoverageIgnore
     */
    private static function codePointToUtf8(int $num): string
    {
        if ($num <= 0x7F) {
            return \chr($num);
        }

        if ($num <= 0x7FF) {
            return \chr(($num>>6) + 0xC0) . \chr(($num&0x3F) + 0x80);
        }

        if ($num <= 0xFFFF) {
            return \chr(($num>>12) + 0xE0) . \chr((($num>>6)&0x3F) + 0x80) . \chr(($num&0x3F) + 0x80);
        }

        if ($num <= 0x1FFFFF) {
            return \chr(($num>>18) + 0xF0) . \chr((($num>>12)&0x3F) + 0x80)
                . \chr((($num>>6)&0x3F) + 0x80) . \chr(($num&0x3F) + 0x80);
        }

        throw new Error('Invalid UTF-8 codepoint escape sequence: Codepoint too large.');
    }
}


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

use Interop\Container\ServiceProviderInterface;
use Psr\Container\ContainerInterface;
use Viserio\Component\Bus\QueueingDispatcher;
use Viserio\Component\Contract\Bus\QueueingDispatcher as QueueingDispatcherContract;
use Viserio\Component\Contract\Queue\Factory as FactoryContract;

class QueueingBusServiceProvider implements ServiceProviderInterface
{
    /**
     * {@inheritdoc}
     */
    public function getFactories(): array
    {
        return [
            QueueingDispatcherContract::class => [self::class, 'registerBusQueueingDispatcher'],
            QueueingDispatcher::class         => static function (ContainerInterface $container) {
                return $container->get(QueueingDispatcherContract::class);
            },
            'bus' => static function (ContainerInterface $container) {
                return $container->get(QueueingDispatcherContract::class);
            },
        ];
    }

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

    /**
     * Create a new QueueingDispatcher instance.
     *
     * @param \Psr\Container\ContainerInterface $container
     *
     * @return \Viserio\Component\Contract\Bus\QueueingDispatcher
     */
    public static function registerBusQueueingDispatcher(ContainerInterface $container): QueueingDispatcherContract
    {
        return new QueueingDispatcher($container, static function ($connection = null) use ($container) {
            return $container->get(FactoryContract::class)->connection($connection);
        });
    }
}


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

use Psr\Http\Message\ServerRequestInterface;
use Throwable;
use Viserio\Component\Contract\Exception\Filter as FilterContract;
use Viserio\Component\Contract\OptionsResolver\RequiresComponentConfig as RequiresComponentConfigContract;
use Viserio\Component\Contract\OptionsResolver\RequiresMandatoryOptions as RequiresMandatoryOptionsContract;
use Viserio\Component\OptionsResolver\Traits\OptionsResolverTrait;

class VerboseFilter implements FilterContract, RequiresComponentConfigContract, RequiresMandatoryOptionsContract
{
    use OptionsResolverTrait;

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

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

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

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

    /**
     * {@inheritdoc}
     */
    public function filter(
        array $displayers,
        ServerRequestInterface $request,
        Throwable $original,
        Throwable $transformed,
        int $code
    ): array {
        if ($this->resolvedOptions['debug'] !== true) {
            foreach ($displayers as $index => $displayer) {
                if ($displayer->isVerbose()) {
                    unset($displayers[$index]);
                }
            }
        }

        return \array_values($displayers);
    }
}


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

use Viserio\Component\Console\Helper\DescriptorHelper;

/**
 * @internal
 */
final class ListCommand extends AbstractCommand
{
    /**
     * {@inheritdoc}
     */
    protected static $defaultName = 'list';

    /**
     * {@inheritdoc}
     */
    protected $signature = 'list
        [namespace? : The namespace name.]
        [--show-description : Show command descriptions on the output list.]
        [--show-hidden : Show hidden commands on the output list.]
        [--raw : To output raw command list.]
        [--format=txt : The output format (txt, xml, json, or md).]
    ';

    /**
     * {@inheritdoc}
     */
    protected $description = 'Lists console commands';

    /**
     * {@inheritdoc}
     */
    protected $hidden = true;

    /**
     * {@inheritdoc}
     */
    public function handle(): int
    {
        $helper = new DescriptorHelper();

        $helper->describe(
            $this->getOutput(),
            $this,
            [
                'format'           => $this->option('format'),
                'raw_text'         => $this->option('raw'),
                'show-description' => $this->option('show-description'),
                'namespace'        => $this->argument('namespace'),
            ]
        );

        return 0;
    }
}


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

use Viserio\Component\Contract\Routing\SegmentMatcher as SegmentMatcherContract;
use Viserio\Component\Routing\TreeGenerator\ChildrenNodeCollection;
use Viserio\Component\Routing\TreeGenerator\RouteTreeNode;

final class RouteTreeOptimizer
{
    /**
     * Optimizes the supplied route tree.
     *
     * @param array $routeTree
     *
     * @return array
     */
    public function optimize(array $routeTree): array
    {
        $segmentDepthNodeMap = $routeTree[1];

        foreach ($segmentDepthNodeMap as $segmentDepth => $nodes) {
            $segmentDepthNodeMap[$segmentDepth] = $this->optimizeNodes($nodes);
        }

        return [$routeTree[0], $segmentDepthNodeMap];
    }

    /**
     * Optimize child node collection.
     *
     * @param \Viserio\Component\Routing\TreeGenerator\ChildrenNodeCollection $nodes
     *
     * @return \Viserio\Component\Routing\TreeGenerator\ChildrenNodeCollection
     */
    private function optimizeNodes(ChildrenNodeCollection $nodes): ChildrenNodeCollection
    {
        $optimizedNodes = [];

        foreach ($nodes->getChildren() as $key => $node) {
            $optimizedNodes[$key] = $this->optimizeNode($node);
        }

        $optimizedNodes = new ChildrenNodeCollection($optimizedNodes);

        return $this->moveCommonMatchersToParentNode($optimizedNodes);
    }

    /**
     * Optimize node collection.
     *
     * @param \Viserio\Component\Routing\TreeGenerator\RouteTreeNode $node
     *
     * @return \Viserio\Component\Routing\TreeGenerator\RouteTreeNode
     */
    private function optimizeNode(RouteTreeNode $node): RouteTreeNode
    {
        $matchers = $node->getMatchers();
        $contents = $node->getContents();

        if ($node->isParentNode() === true) {
            $contents = $this->optimizeNodes($node->getContents());
            $children = $contents->getChildren();

            if (\count($children) === 1) {
                $childNode = \reset($children);

                if ($childNode !== false) {
                    $matchers = MatcherOptimizer::mergeMatchers($node->getMatchers(), $childNode->getMatchers());
                    $contents = $childNode->getContents();
                }
            }
        }

        $matchers = MatcherOptimizer::optimizeMatchers($matchers);

        return $node->update($matchers, $contents);
    }

    /**
     * Move matched common node to the parent node.
     *
     * @param \Viserio\Component\Routing\TreeGenerator\ChildrenNodeCollection $nodeCollection
     *
     * @return \Viserio\Component\Routing\TreeGenerator\ChildrenNodeCollection
     */
    private function moveCommonMatchersToParentNode(ChildrenNodeCollection $nodeCollection): ChildrenNodeCollection
    {
        $nodes = $nodeCollection->getChildren();

        if (\count($nodes) <= 1) {
            return $nodeCollection;
        }

        $children = [];
        $previous = \array_shift($nodes);

        foreach ($nodes as $node) {
            $parent = $this->extractCommonParentNode($previous, $node);

            if ($parent) {
                $previous = $parent;
            } else {
                $children[] = $previous;
                $previous   = $node;
            }
        }

        $children[] = $previous;

        return new ChildrenNodeCollection($children);
    }

    /**
     * Extract parent nodes from route tree nood.
     *
     * @param \Viserio\Component\Routing\TreeGenerator\RouteTreeNode $node1
     * @param \Viserio\Component\Routing\TreeGenerator\RouteTreeNode $node2
     *
     * @return null|\Viserio\Component\Routing\TreeGenerator\RouteTreeNode
     */
    private function extractCommonParentNode(RouteTreeNode $node1, RouteTreeNode $node2): ?RouteTreeNode
    {
        $matcherCompare = static function (SegmentMatcherContract $matcher, SegmentMatcherContract $matcher2) {
            return \strcmp($matcher->getHash(), $matcher2->getHash());
        };

        $commonMatchers = \array_uintersect_assoc($node1->getMatchers(), $node2->getMatchers(), $matcherCompare);

        if (\count($commonMatchers) === 0) {
            return null;
        }

        $children = [];
        $nodes    = [$node1, $node2];

        foreach ($nodes as $node) {
            $specificMatchers  = \array_udiff_assoc($node->getMatchers(), $commonMatchers, $matcherCompare);
            $duplicateMatchers = \array_uintersect_assoc($node->getMatchers(), $commonMatchers, $matcherCompare);

            foreach ($duplicateMatchers as $segmentDepth => $matcher) {
                $commonMatchers[$segmentDepth]->mergeParameterKeys($matcher);
            }

            if (\count($specificMatchers) === 0 && $node->isParentNode()) {
                foreach ($node->getContents()->getChildren() as $childNode) {
                    $children[] = $childNode;
                }
            } else {
                $children[] = $node->update($specificMatchers, $node->getContents());
            }
        }

        return new RouteTreeNode($commonMatchers, new ChildrenNodeCollection($children));
    }
}


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

class TracedStatement
{
    protected $sql;

    protected $rowCount;

    protected $parameters;

    protected $startTime;

    protected $endTime;

    protected $duration;

    protected $startMemory;

    protected $endMemory;

    protected $memoryDelta;

    protected $exception;

    protected $preparedId;

    /**
     * @param string $sql
     * @param array  $params
     * @param string $preparedId
     */
    public function __construct($sql, array $params = [], $preparedId = null)
    {
        $this->sql        = $sql;
        $this->parameters = $this->checkParameters($params);
        $this->preparedId = $preparedId;
    }

    /**
     * Returns the SQL string used for the query.
     *
     * @return string
     */
    public function getSql(): string
    {
        return $this->sql;
    }

    /**
     * Returns the number of rows affected/returned.
     *
     * @return int
     */
    public function getRowCount(): int
    {
        return $this->rowCount;
    }

    /**
     * Returns an array of parameters used with the query.
     *
     * @return array
     */
    public function getParameters(): array
    {
        $params = [];

        foreach ($this->parameters as $name => $param) {
            $params[$name] = \htmlentities($param, \ENT_QUOTES, 'UTF-8', false);
        }

        return $params;
    }

    /**
     * @return mixed
     */
    public function getStartTime()
    {
        return $this->startTime;
    }

    /**
     * @return mixed
     */
    public function getEndTime()
    {
        return $this->endTime;
    }

    /**
     * Returns the duration in seconds of the execution.
     *
     * @return int
     */
    public function getDuration(): int
    {
        return $this->duration;
    }

    /**
     * @return mixed
     */
    public function getStartMemory()
    {
        return $this->startMemory;
    }

    /**
     * @return mixed
     */
    public function getEndMemory()
    {
        return $this->endMemory;
    }

    /**
     * Returns the exception triggered.
     *
     * @return \Exception
     */
    public function getException(): \Exception
    {
        return $this->exception;
    }

    /**
     * Returns the prepared statement id.
     *
     * @return string
     */
    public function getPreparedId(): string
    {
        return $this->preparedId;
    }

    /**
     * @param null $startTime
     * @param null $startMemory
     */
    public function start($startTime = null, $startMemory = null): void
    {
        $this->startTime   = $startTime   ?? \microtime(true);
        $this->startMemory = $startMemory ?? \memory_get_usage(true);
    }

    /**
     * @param null|\Exception $exception
     * @param int             $rowCount
     * @param null            $endTime
     * @param null            $endMemory
     */
    public function end(\Exception $exception = null, $rowCount = 0, $endTime = null, $endMemory = null): void
    {
        $this->endTime     = $endTime ?? \microtime(true);
        $this->duration    = $this->endTime - $this->startTime;
        $this->endMemory   = $endMemory ?? \memory_get_usage(true);
        $this->memoryDelta = $this->endMemory - $this->startMemory;
        $this->exception   = $exception;
        $this->rowCount    = $rowCount;
    }

    /**
     * Check parameters for illegal (non UTF-8) strings, like Binary data.
     *
     * @param array $params
     *
     * @return mixed
     */
    public function checkParameters(array $params)
    {
        foreach ($params as &$param) {
            if (! \mb_check_encoding($param, 'UTF-8')) {
                $param = '[BINARY DATA]';
            }
        }

        return $params;
    }

    /**
     * Returns the SQL string with any parameters used embedded.
     *
     * @param string $quotationChar
     *
     * @return string
     */
    public function getSqlWithParams($quotationChar = '<>'): string
    {
        if (($l = \mb_strlen($quotationChar)) > 1) {
            $quoteLeft  = \mb_substr($quotationChar, 0, $l / 2);
            $quoteRight = \mb_substr($quotationChar, $l / 2);
        } else {
            $quoteLeft = $quoteRight = $quotationChar;
        }
        $sql = $this->sql;

        foreach ($this->parameters as $k => $v) {
            $v = "{$quoteLeft}{$v}{$quoteRight}";

            if (! \is_numeric($k)) {
                $sql = \str_replace($k, $v, $sql);
            } else {
                $p   = \mb_strpos($sql, '?');
                $sql = \mb_substr($sql, 0, $p) . $v . \mb_substr($sql, $p + 1);
            }
        }

        return $sql;
    }

    /**
     * Checks if this is a prepared statement.
     *
     * @return bool
     */
    public function isPrepared(): bool
    {
        return $this->preparedId !== null;
    }

    /**
     * Returns the memory usage during the execution.
     *
     * @return int
     */
    public function getMemoryUsage(): int
    {
        return $this->memoryDelta;
    }

    /**
     * Checks if the statement was successful.
     *
     * @return bool
     */
    public function isSuccess(): bool
    {
        return $this->exception === null;
    }

    /**
     * Returns the exception's code.
     *
     * @return string
     */
    public function getErrorCode(): string
    {
        return $this->exception !== null ? $this->exception->getCode() : 0;
    }

    /**
     * Returns the exception's message.
     *
     * @return string
     */
    public function getErrorMessage(): string
    {
        return $this->exception !== null ? $this->exception->getMessage() : '';
    }
}


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

use Iterator;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UploadedFileInterface;
use Throwable;
use Viserio\Component\Contract\Http\Exception\InvalidArgumentException;
use Viserio\Component\Contract\Http\Exception\RuntimeException;
use Viserio\Component\Http\Stream\PumpStream;

/**
 * Some code in this class it taken from zend-diactoros.
 *
 * See the original here: https://github.com/zendframework/zend-diactoros/blob/master/src/ServerRequestFactory.php
 *
 * @copyright Copyright (c) 2015-2016 Zend Technologies USA Inc. (http://www.zend.com)
 * @license   http://framework.zend.com/license/new-bsd New BSD License
 */
final class Util
{
    /**
     * Private constructor; non-instantiable.
     *
     * @codeCoverageIgnore
     */
    private function __construct()
    {
    }

    /**
     * Returns headers obtained from the SAPI (generally `$_SERVER`).
     *
     * @param array $server
     *
     * @return array
     */
    public static function getAllHeaders(array $server): array
    {
        $headers    = [];

        foreach ($server as $key => $value) {
            // Apache prefixes environment variables with REDIRECT_
            // if they are added by rewrite rules
            if (\strpos($key, 'REDIRECT_') === 0) {
                $key = \substr($key, 9);
                // We will not overwrite existing variables with the
                // prefixed versions, though
                if (\array_key_exists($key, $server)) {
                    continue;
                }
            }

            if ($value === '') {
                continue;
            }

            if (\strpos($key, 'HTTP_') === 0) {
                $key = \substr($key, 5);

                if (! isset($_SERVER[$key])) {
                    $name           = \str_replace(' ', '-', \ucwords(\strtolower(\str_replace('_', ' ', $key))));
                    $headers[$name] = $value;
                }

                continue;
            }

            if (\strpos($key, 'CONTENT_') === 0) {
                $name           = \str_replace(' ', '-', \ucwords(\strtolower(\str_replace('_', ' ', $key))));
                $headers[$name] = $value;
            }
        }

        if (! isset($headers['Authorization'])) {
            if (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
                $headers['Authorization'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
            } elseif (isset($_SERVER['PHP_AUTH_USER'])) {
                $basic_pass               = $_SERVER['PHP_AUTH_PW'] ?? '';
                $headers['Authorization'] = 'Basic ' . \base64_encode($_SERVER['PHP_AUTH_USER'] . ':' . $basic_pass);
            } elseif (isset($_SERVER['PHP_AUTH_DIGEST'])) {
                $headers['Authorization'] = $_SERVER['PHP_AUTH_DIGEST'];
            }
        }

        return $headers;
    }

    /**
     * Safely opens a PHP stream resource using a filename.
     *
     * When fopen fails, PHP normally raises a warning. This function adds an
     * error handler that checks for errors and throws an exception instead.
     *
     * @param string $filename File to open
     * @param string $mode     Mode used to open the file
     *
     * @throws \Viserio\Component\Contract\Http\Exception\RuntimeException if the file cannot be opened
     *
     * @return false|resource
     */
    public static function tryFopen(string $filename, string $mode)
    {
        $ex = null;

        \set_error_handler(static function () use ($filename, $mode, &$ex): void {
            $ex = new RuntimeException(\sprintf(
                'Unable to open [%s] using mode %s: %s',
                $filename,
                $mode,
                \func_get_args()[1]
            ));
        });

        $handle = \fopen($filename, $mode);

        \restore_error_handler();

        if ($ex instanceof Throwable) {
            // @var $ex \RuntimeException
            throw $ex;
        }

        return $handle;
    }

    /**
     * Create a new stream based on the input type.
     *
     * Options is an associative array that can contain the following keys:
     * - metadata: Array of custom metadata.
     * - size: Size of the stream.
     *
     * @param null|bool|callable|float|int|\Iterator|resource|StreamInterface|string $resource Entity body data
     * @param array                                                                  $options  Additional options
     *
     * @throws \Viserio\Component\Contract\Http\Exception\InvalidArgumentException if the $resource arg is not valid
     *
     * @return \Psr\Http\Message\StreamInterface
     */
    public static function createStreamFor($resource = '', array $options = []): StreamInterface
    {
        if (\is_scalar($resource)) {
            $stream = self::tryFopen('php://temp', 'r+');

            if ($resource !== '') {
                \fwrite($stream, (string) $resource);
                \fseek($stream, 0);
            }

            return new Stream($stream, $options);
        }

        $type = \gettype($resource);

        if ($type === 'resource') {
            return new Stream($resource, $options);
        }

        if ($type === 'object') {
            if ($resource instanceof StreamInterface) {
                return $resource;
            }

            if ($resource instanceof Iterator) {
                return new PumpStream(static function () use ($resource) {
                    if (! $resource->valid()) {
                        return false;
                    }

                    $result = $resource->current();
                    $resource->next();

                    return $result;
                }, $options);
            }

            if (\method_exists($resource, '__toString')) {
                return self::createStreamFor($resource->__toString(), $options);
            }
        }

        if ($type === 'NULL') {
            return new Stream(self::tryFopen('php://temp', 'r+'), $options);
        }

        if (\is_callable($resource)) {
            return new PumpStream($resource, $options);
        }

        throw new InvalidArgumentException('Invalid resource type: ' . \gettype($resource));
    }

    /**
     * Copy the contents of a stream into a string until the given number of
     * bytes have been read.
     *
     * @param \Psr\Http\Message\StreamInterface $stream Stream to read
     * @param int                               $maxLen Maximum number of bytes to read. Pass -1
     *                                                  to read the entire stream.
     *
     * @throws \RuntimeException
     *
     * @return string
     */
    public static function copyToString(StreamInterface $stream, int $maxLen = -1): string
    {
        $buffer = '';

        if ($maxLen === -1) {
            while (! $stream->eof()) {
                $buf = $stream->read(1048576);
                // Using a loose equality here to match on '' and false.
                if (empty($buf)) {
                    break;
                }

                $buffer .= $buf;
            }

            return $buffer;
        }

        $len = 0;

        while (! $stream->eof() && $len < $maxLen) {
            $buf = $stream->read($maxLen - $len);
            // Using a loose equality here to match on '' and false.
            if (empty($buf)) {
                break;
            }

            $buffer .= $buf;
            $len = \strlen($buffer);
        }

        return $buffer;
    }

    /**
     * Copy the contents of a stream into another stream until the given number
     * of bytes have been read.
     *
     * @param \Psr\Http\Message\StreamInterface $source Stream to read from
     * @param \Psr\Http\Message\StreamInterface $dest   Stream to write to
     * @param int                               $maxLen Maximum number of bytes to read. Pass -1
     *                                                  to read the entire stream.
     *
     * @throws \RuntimeException
     */
    public static function copyToStream(StreamInterface $source, StreamInterface $dest, int $maxLen = -1): void
    {
        if ($maxLen === -1) {
            while (! $source->eof()) {
                if (! (bool) $dest->write($source->read(1048576))) {
                    break;
                }
            }

            return;
        }

        $bufferSize = 8192;

        if ($maxLen === -1) {
            while (! $source->eof()) {
                if (! (bool) $dest->write($source->read($bufferSize))) {
                    break;
                }
            }
        } else {
            $remaining = $maxLen;

            while ($remaining > 0 && ! $source->eof()) {
                $buf = $source->read(\min($bufferSize, $remaining));
                $len = \strlen($buf);

                if (! $len) {
                    break;
                }

                $remaining -= $len;
                $dest->write($buf);
            }
        }
    }

    /**
     * Read a line from the stream up to the maximum allowed buffer length.
     *
     * @param \Psr\Http\Message\StreamInterface $stream    Stream to read from
     * @param int                               $maxLength Maximum buffer length
     *
     * @return string
     */
    public static function readline(StreamInterface $stream, int $maxLength = null): string
    {
        $buffer = '';
        $size   = 0;

        while (! $stream->eof()) {
            $byte = $stream->read(1);
            // Using a loose equality here to match on '' and false.
            if ($byte === '' || $byte === false || $byte === null) {
                return $buffer;
            }

            $buffer .= $byte;

            // Break when a new line is found or the max length - 1 is reached
            if ($byte === "\n" || ++$size === $maxLength - 1) {
                break;
            }
        }

        return $buffer;
    }

    /**
     * Return an UploadedFile instance array.
     *
     * @param array $files A array which respect $_FILES structure
     *
     * @throws \Viserio\Component\Contract\Http\Exception\InvalidArgumentException for unrecognized values
     *
     * @return array
     */
    public static function normalizeFiles(array $files): array
    {
        /**
         * @param array[]|string[]      $tmpNameTree
         * @param array[]|int[]         $sizeTree
         * @param array[]|int[]         $errorTree
         * @param null|array[]|string[] $nameTree
         * @param null|array[]|string[] $typeTree
         *
         * @return array[]|\Psr\Http\Message\UploadedFileInterface[]
         */
        $recursiveNormalize = static function (
            array $tmpNameTree,
            array $sizeTree,
            array $errorTree,
            array $nameTree = null,
            array $typeTree = null
        ) use (&$recursiveNormalize) {
            $normalized = [];

            foreach ($tmpNameTree as $key => $value) {
                if (\is_array($value)) {
                    // Traverse
                    $normalized[$key] = $recursiveNormalize(
                        $tmpNameTree[$key],
                        $sizeTree[$key],
                        $errorTree[$key],
                        $nameTree[$key] ?? null,
                        $typeTree[$key] ?? null
                    );

                    continue;
                }

                $normalized[$key] = self::createUploadedFileFromSpec([
                    'tmp_name' => $tmpNameTree[$key],
                    'size'     => $sizeTree[$key],
                    'error'    => $errorTree[$key],
                    'name'     => $nameTree[$key] ?? null,
                    'type'     => $typeTree[$key] ?? null,
                ]);
            }

            return $normalized;
        };

        /**
         * Normalize an array of file specifications.
         *
         * Loops through all nested files (as determined by receiving an array to the
         * `tmp_name` key of a `$_FILES` specification) and returns a normalized array
         * of UploadedFile instances.
         *
         * This function normalizes a `$_FILES` array representing a nested set of
         * uploaded files as produced by the php-fpm SAPI, CGI SAPI, or mod_php
         * SAPI.
         *
         * @param array $files
         *
         * @return \Psr\Http\Message\UploadedFileInterface[]
         */
        $normalizeUploadedFileSpecification = static function (array $files = []) use (&$recursiveNormalize) {
            if (! isset($files['tmp_name']) || ! \is_array($files['tmp_name']) ||
                ! isset($files['size']) || ! \is_array($files['size']) ||
                ! isset($files['error']) || ! \is_array($files['error'])
            ) {
                throw new InvalidArgumentException(\sprintf(
                    '$files provided to %s MUST contain each of the keys "tmp_name",'
                    . ' "size", and "error", with each represented as an array;'
                    . ' one or more were missing or non-array values',
                    __FUNCTION__
                ));
            }

            return $recursiveNormalize(
                $files['tmp_name'],
                $files['size'],
                $files['error'],
                $files['name'] ?? null,
                $files['type'] ?? null
            );
        };

        $normalized = [];

        foreach ($files as $key => $value) {
            if ($value instanceof UploadedFileInterface) {
                $normalized[$key] = $value;

                continue;
            }

            if (\is_array($value) && isset($value['tmp_name']) && \is_array($value['tmp_name'])) {
                $normalized[$key] = $normalizeUploadedFileSpecification($value);

                continue;
            }

            if (\is_array($value) && isset($value['tmp_name'])) {
                $normalized[$key] = self::createUploadedFileFromSpec($value);

                continue;
            }

            if (\is_array($value)) {
                $normalized[$key] = self::normalizeFiles($value);

                continue;
            }

            throw new InvalidArgumentException('Invalid value in files specification.');
        }

        return $normalized;
    }

    /**
     * Create and return an UploadedFile instance from a $_FILES specification.
     *
     * If the specification represents an array of values, this method will
     * delegate to normalizeNestedFileSpec() and return that return value.
     *
     * @param array $value $_FILES struct
     *
     * @return array|UploadedFileInterface
     */
    private static function createUploadedFileFromSpec(array $value)
    {
        if (\is_array($value['tmp_name'])) {
            return self::normalizeNestedFileSpec($value);
        }

        return new UploadedFile(
            $value['tmp_name'],
            (int) $value['size'],
            (int) $value['error'],
            $value['name'],
            $value['type']
        );
    }

    /**
     * Normalize an array of file specifications.
     *
     * Loops through all nested files and returns a normalized array of
     * UploadedFileInterface instances.
     *
     * @param array $files
     *
     * @return UploadedFileInterface[]
     */
    private static function normalizeNestedFileSpec(array $files = []): array
    {
        $normalizedFiles = [];

        foreach (\array_keys($files['tmp_name']) as $key) {
            $spec = [
                'tmp_name' => $files['tmp_name'][$key],
                'size'     => $files['size'][$key],
                'error'    => $files['error'][$key],
                'name'     => $files['name'][$key],
                'type'     => $files['type'][$key],
            ];

            $normalizedFiles[$key] = self::createUploadedFileFromSpec($spec);
        }

        return $normalizedFiles;
    }
}


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

use Viserio\Component\Contract\Routing\Exception\InvalidArgumentException;

class StaticMatcher extends AbstractMatcher
{
    /**
     * The static string.
     *
     * @var string
     */
    protected $segment;

    /**
     * Create a new satic segment matcher instance.
     *
     * @param string     $segment
     * @param null|array $parameterKeys
     *
     * @throws \Viserio\Component\Contract\Routing\Exception\InvalidArgumentException
     */
    public function __construct(string $segment, array $parameterKeys = null)
    {
        if (\strpos($segment, '/') !== false) {
            throw new InvalidArgumentException(
                \sprintf('Cannot create %s: segment cannot contain \'/\', \'%s\' given.', __CLASS__, $segment)
            );
        }

        $this->parameterKeys = $parameterKeys ?? [];
        $this->segment       = $segment;
    }

    /**
     * {@inheritdoc}
     */
    public function getConditionExpression(string $segmentVariable, int $uniqueKey = null): string
    {
        return $segmentVariable . ' === \'' . $this->segment . '\'';
    }

    /**
     * {@inheritdoc}
     */
    public function getMatchedParameterExpressions(string $segmentVariable, int $uniqueKey = null): array
    {
        $keys = $this->parameterKeys;

        if (\count($keys) > 0) {
            return [$keys[0] => $segmentVariable];
        }

        return [];
    }

    /**
     * {@inheritdoc}
     */
    protected function getMatchHash(): string
    {
        return $this->segment;
    }
}


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

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

class JsonDumper implements DumperContract
{
    /**
     * User specified recursion depth.
     *
     * @var int
     */
    private $depth = 512;

    /**
     * Bitmask of JSON decode options.
     *
     * @var int
     */
    private $options = \JSON_PRETTY_PRINT;

    /**
     * Set the user specified recursion depth.
     *
     * @param int $depth
     *
     * @return void
     */
    public function setDepth(int $depth): void
    {
        $this->depth = $depth;
    }

    /**
     * Set the user specified recursion depth.
     *
     * @param int $options
     *
     * @return void
     */
    public function setOptions(int $options): void
    {
        $this->options = $options;
    }

    /**
     * {@inheritdoc}
     */
    public function dump(array $data): string
    {
        // Clear json_last_error()
        \json_encode(null);

        $json = \json_encode($data, $this->options, $this->depth);

        if (\json_last_error() !== \JSON_ERROR_NONE) {
            throw new DumpException(\sprintf('JSON dumping failed: %s.', \json_last_error_msg()));
        }

        $json = \preg_replace('/\[\s+\]/', '[]', $json);

        return \preg_replace('/\{\s+\}/', '{}', $json);
    }
}


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

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

class RoutingDataCollector extends AbstractDataCollector implements PanelAwareContract
{
    /**
     * Router instance.
     *
     * @var \Viserio\Component\Contract\Routing\RouteCollection
     */
    protected $routes;

    /**
     * Create a new viserio routes data collector.
     *
     * @param \Viserio\Component\Contract\Routing\RouteCollection $routes
     */
    public function __construct(RouteCollectionContract $routes)
    {
        $this->routes = $routes;
    }

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

    /**
     * {@inheritdoc}
     */
    public function getMenu(): array
    {
        return [
            'icon'  => \file_get_contents(\dirname(__DIR__, 1) . \DIRECTORY_SEPARATOR . 'Resource' . \DIRECTORY_SEPARATOR . 'icons' . \DIRECTORY_SEPARATOR . 'ic_directions_white_24px.svg'),
            'label' => 'Routes',
            'value' => $this->data['counted'],
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function getPanel(): string
    {
        $headers = [0 => 'Methods', 2 => 'Path', 3 => 'Name', 4 => 'Action', 5 => 'With Middleware', 6 => 'Without Middleware'];
        $data    = [];

        foreach ($this->data['routes'] as $route) {
            $routeData = [
                0 => \implode(' | ', $route->getMethods()),
                2 => $route->getUri(),
                3 => $route->getName() ?? '-',
                4 => $route->getActionName(),
                5 => \implode(', ', $route->gatherMiddleware()),
                6 => \implode(', ', $route->gatherDisabledMiddleware()),
            ];

            if ($route->getDomain() !== null) {
                $routeData[1] = 'Domain';
            }

            $data[] = $routeData;
        }

        if (isset($data[0][1])) {
            $headers[1] = 'Domain';
        }

        \sort($data, \SORT_NUMERIC);
        \sort($headers, \SORT_NUMERIC);

        return $this->createTable(
            $data,
            [
                'headers'   => $headers,
                'vardumper' => false,
            ]
        );
    }
}


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

use Viserio\Component\Contract\Cookie\Cookie as CookieContract;
use Viserio\Component\Contract\Cookie\QueueingFactory as JarContract;

class CookieJar implements JarContract
{
    /**
     * The default path (if specified).
     *
     * @var string
     */
    protected $path = '/';

    /**
     * The default domain (if specified).
     *
     * @var null|string
     */
    protected $domain;

    /**
     * The default secure setting.
     *
     * @var bool
     */
    protected $secure = false;

    /**
     * All of the cookies queued for sending.
     *
     * @var array
     */
    protected $queued = [];

    /**
     * {@inheritdoc}
     */
    public function hasQueued(string $key): bool
    {
        return $this->queued($key) !== null;
    }

    /**
     * {@inheritdoc}
     */
    public function queued(string $key, $default = null): ?CookieContract
    {
        return $this->queued[$key] ?? $default;
    }

    /**
     * {@inheritdoc}
     */
    public function create(
        string $name,
        ?string $value  = null,
        int $second     = 0,
        ?string $path   = null,
        ?string $domain = null,
        bool $secure    = false,
        bool $httpOnly  = true,
        $sameSite       = false
    ): CookieContract {
        [$path, $domain, $secure] = $this->getPathAndDomain($path, $domain, $secure);

        return new SetCookie($name, $value, $second, $path, $domain, $secure, $httpOnly, $sameSite);
    }

    /**
     * {@inheritdoc}
     */
    public function forever(
        string $name,
        ?string $value  = null,
        ?string $path   = null,
        ?string $domain = null,
        bool $secure    = false,
        bool $httpOnly  = true,
        $sameSite       = false
    ): CookieContract {
        return $this->create($name, $value, 2628000, $path, $domain, $secure, $httpOnly, $sameSite);
    }

    /**
     * {@inheritdoc}
     */
    public function delete(string $name, ?string $path = null, ?string $domain = null): CookieContract
    {
        return $this->create($name, null, -2628000, $path, $domain);
    }

    /**
     * {@inheritdoc}
     */
    public function queue(...$arguments): void
    {
        if (\reset($arguments) instanceof CookieContract) {
            $cookie = \reset($arguments);
        } else {
            $cookie = $this->create(...$arguments);
        }

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

    /**
     * {@inheritdoc}
     */
    public function unqueue(string $name): void
    {
        unset($this->queued[$name]);
    }

    /**
     * Set the default path and domain for the jar.
     *
     * @param string $path
     * @param string $domain
     * @param bool   $secure
     *
     * @return $this
     */
    public function setDefaultPathAndDomain(string $path, string $domain, bool $secure = false): self
    {
        [$this->path, $this->domain, $this->secure] = [$path, $domain, $secure];

        return $this;
    }

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

    /**
     * Get the path and domain, or the default values.
     *
     * @param null|string $path
     * @param null|string $domain
     * @param bool        $secure
     *
     * @return array
     */
    protected function getPathAndDomain(?string $path, ?string $domain, bool $secure = false): array
    {
        return [$path ?? $this->path, $domain ?? $this->domain, $secure ?? $this->secure];
    }
}


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

use Psr\Http\Message\ResponseFactoryInterface;
use Viserio\Component\Contract\OptionsResolver\ProvidesDefaultOptions as ProvidesDefaultOptionsContract;
use Viserio\Component\Contract\OptionsResolver\RequiresComponentConfig as RequiresComponentConfigContract;
use Viserio\Component\OptionsResolver\Traits\OptionsResolverTrait;
use Whoops\Handler\Handler;
use Whoops\Handler\PrettyPageHandler;

class WhoopsPrettyDisplayer extends AbstractWhoopsDisplayer implements
    RequiresComponentConfigContract,
    ProvidesDefaultOptionsContract
{
    use OptionsResolverTrait;

    /**
     * Configurations list for whoops.
     *
     * @var array
     */
    private $resolvedOptions;

    /**
     * Create a new whoops displayer instance.
     *
     * @param \Psr\Http\Message\ResponseFactoryInterface $responseFactory
     * @param array|\ArrayAccess                         $config
     */
    public function __construct(ResponseFactoryInterface $responseFactory, $config = [])
    {
        parent::__construct($responseFactory);

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

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

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

    /**
     * {@inheritdoc}
     */
    public function getContentType(): string
    {
        return 'text/html';
    }

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

    /**
     * Get the Whoops handler.
     *
     * @return \Whoops\Handler\Handler
     */
    protected function getHandler(): Handler
    {
        $handler = new PrettyPageHandler();

        $handler->handleUnconditionally(true);

        foreach ($this->resolvedOptions['blacklist'] as $key => $secrets) {
            foreach ($secrets as $secret) {
                $handler->blacklist($key, $secret);
            }
        }

        $handler->setApplicationPaths($this->resolvedOptions['application_paths']);

        return $handler;
    }
}


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

use Fig\Http\Message\RequestMethodInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\UriInterface;
use Viserio\Component\Contract\Http\Exception\InvalidArgumentException;

class Request extends AbstractMessage implements RequestInterface, RequestMethodInterface
{
    public const METHOD_LINK    = 'LINK';
    public const METHOD_UNLINK  = 'UNLINK';

    /**
     * The request method.
     *
     * @var string
     */
    protected $method;

    /**
     * The request URI target (path + query string).
     *
     * @var string
     */
    protected $requestTarget;

    /**
     * The request URI object.
     *
     * @var null|\Psr\Http\Message\UriInterface
     */
    protected $uri;

    /**
     * Create a new request instance.
     *
     * @param null|string|UriInterface                               $uri     uri for the request
     * @param null|string                                            $method  http method for the request
     * @param array                                                  $headers headers for the message
     * @param null|\Psr\Http\Message\StreamInterface|resource|string $body    message body
     * @param string                                                 $version http protocol version
     */
    public function __construct(
        $uri,
        ?string $method = self::METHOD_GET,
        array $headers  = [],
        $body           = null,
        string $version = '1.1'
    ) {
        $this->method = $this->filterMethod($method);
        $this->uri    = $this->createUri($uri);
        $this->setHeaders($headers);
        $this->protocol = $version;

        // per PSR-7: attempt to set the Host header from a provided URI if no
        // Host header is provided
        if (! $this->hasHeader('Host') && $this->uri->getHost()) {
            $this->updateHostFromUri();
        }

        if ($body !== '' && $body !== null) {
            $this->stream = Util::createStreamFor($body);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function getMethod(): string
    {
        return $this->method;
    }

    /**
     * {@inheritdoc}
     */
    public function getRequestTarget(): string
    {
        if ($this->requestTarget !== null) {
            return $this->requestTarget;
        }

        $target = $this->uri->getPath();

        if ($target === '') {
            $target = '/';
        }

        if ($this->uri->getQuery() !== '') {
            $target .= '?' . $this->uri->getQuery();
        }

        return $target;
    }

    /**
     * {@inheritdoc}
     */
    public function getUri(): UriInterface
    {
        return $this->uri;
    }

    /**
     * {@inheritdoc}
     */
    public function withRequestTarget($requestTarget): RequestInterface
    {
        if (\preg_match('#\s#', $requestTarget)) {
            throw new InvalidArgumentException(
                'Invalid request target provided; cannot contain whitespace.'
            );
        }

        $new                = clone $this;
        $new->requestTarget = $requestTarget;

        return $new;
    }

    /**
     * {@inheritdoc}
     */
    public function withMethod($method): RequestInterface
    {
        $method = $this->filterMethod($method);

        $new         = clone $this;
        $new->method = $method;

        return $new;
    }

    /**
     * {@inheritdoc}
     */
    public function withUri(UriInterface $uri, $preserveHost = false): RequestInterface
    {
        if ($this->uri === $uri) {
            return $this;
        }

        $new      = clone $this;
        $new->uri = $uri;

        if ($preserveHost && $this->hasHeader('Host')) {
            return $new;
        }

        $new->updateHostFromUri();

        return $new;
    }

    /**
     * Retrieve the host from the URI instance.
     *
     * @return void
     */
    private function updateHostFromUri(): void
    {
        $host = $this->uri->getHost();

        if ($host === '') {
            return;
        }

        if (($port = $this->uri->getPort()) !== null) {
            $host .= ':' . $port;
        }

        $this->headerNames['host'] = 'Host';

        // Remove an existing host header if present, regardless of current
        // de-normalization of the header name.
        // @see https://github.com/zendframework/zend-diactoros/issues/91
        foreach (\array_keys($this->headers) as $oldHeader) {
            if (\strtolower($oldHeader) === 'host') {
                unset($this->headers[$oldHeader]);
            }
        }

        // Ensure Host is the first header.
        // See: http://tools.ietf.org/html/rfc7230#section-5.4
        $this->headers = ['Host' => [$host]] + $this->headers;
    }

    /**
     * Validate the HTTP method.
     *
     * @param null|string $method
     *
     * @throws \Viserio\Component\Contract\Http\Exception\InvalidArgumentException on invalid HTTP method
     *
     * @return string
     */
    private function filterMethod(?string $method): string
    {
        if ($method === null) {
            return self::METHOD_GET;
        }

        if (! \preg_match("/^[!#$%&'*+.^_`|~0-9a-z-]+$/i", $method)) {
            throw new InvalidArgumentException(\sprintf(
                'Unsupported HTTP method [%s].',
                $method
            ));
        }

        return $method;
    }

    /**
     * Create and return a URI instance.
     *
     * If `$uri` is a already a `UriInterface` instance, returns it.
     *
     * If `$uri` is a string, passes it to the `Uri` constructor to return an
     * instance.
     *
     * If `$uri is null, creates and returns an empty `Uri` instance.
     *
     * Otherwise, it raises an exception.
     *
     * @param null|string|UriInterface $uri
     *
     * @throws \Viserio\Component\Contract\Http\Exception\InvalidArgumentException
     *
     * @return \Psr\Http\Message\UriInterface
     */
    private function createUri($uri): UriInterface
    {
        if ($uri instanceof UriInterface) {
            return $uri;
        }

        if (\is_string($uri)) {
            return Uri::createFromString($uri);
        }

        if ($uri === null) {
            return Uri::createFromString();
        }

        throw new InvalidArgumentException(
            'Invalid URI provided; must be null, a string or a [\Psr\Http\Message\UriInterface] instance.'
        );
    }
}


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

trait FilesystemExtensionTrait
{
    /**
     * Extract the file extension from a file path.
     *
     * @param string $path
     *
     * @return string
     */
    public function getExtension(string $path): string
    {
        return \pathinfo($this->getTransformedPath($path), \PATHINFO_EXTENSION);
    }

    /**
     * Returns the filename without the extension from a file path.
     *
     * @param string      $path      The path string
     * @param null|string $extension If specified, only that extension is cut off
     *                               (may contain leading dot)
     *
     * @return string Filename without extension
     */
    public function withoutExtension(string $path, string $extension = null): string
    {
        $path = $this->getTransformedPath($path);

        if ($extension !== null) {
            // remove extension and trailing dot
            return \rtrim(\basename($path, $extension), '.');
        }

        return \pathinfo($path, \PATHINFO_FILENAME);
    }

    /**
     * Changes the extension of a path string.
     *
     * @param string $path      The path string with filename.ext to change
     * @param string $extension New extension (with or without leading dot)
     *
     * @return string The path string with new file extension
     */
    public function changeExtension(string $path, string $extension): string
    {
        $path = $this->getTransformedPath($path);

        $explode    = \explode('.', $path);
        $substrPath = \mb_substr($path, -1);

        // No extension for paths
        if ($substrPath === '/' || \is_dir($path)) {
            return $path;
        }

        $actualExtension = null;
        $extension       = \ltrim($extension, '.');

        if (\count($explode) >= 2 && ! \is_dir($path)) {
            $actualExtension = \mb_strtolower($extension);
        }

        // No actual extension in path
        if ($actualExtension === null) {
            return $path . ($substrPath === '.' ? '' : '.') . $extension;
        }

        return \mb_substr($path, 0, -\mb_strlen($actualExtension)) . $extension;
    }
}


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

use Interop\Container\ServiceProviderInterface;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Container\ContainerInterface;
use Psr\SimpleCache\CacheInterface;
use Viserio\Component\Cache\CacheManager;
use Viserio\Component\Contract\Cache\Manager as CacheManagerContract;

class CacheServiceProvider implements ServiceProviderInterface
{
    /**
     * {@inheritdoc}
     */
    public function getFactories(): array
    {
        return [
            CacheManagerContract::class => [self::class, 'createCacheManager'],
            CacheManager::class         => static function (ContainerInterface $container): CacheManagerContract {
                return $container->get(CacheManagerContract::class);
            },
            'cache' => static function (ContainerInterface $container): CacheManagerContract {
                return $container->get(CacheManagerContract::class);
            },
            CacheItemPoolInterface::class => [self::class, 'registerDefaultCache'],
            CacheInterface::class         => [self::class, 'registerDefaultCache'],
            'cache.store'                 => static function (ContainerInterface $container) {
                return $container->get(CacheItemPoolInterface::class);
            },
        ];
    }

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

    /**
     * A instance of the default driver.
     *
     * @param \Psr\Container\ContainerInterface $container
     *
     * @return \Psr\Cache\CacheItemPoolInterface|\Psr\SimpleCache\CacheInterface
     */
    public static function registerDefaultCache(ContainerInterface $container)
    {
        return $container->get(CacheManager::class)->getDriver();
    }

    /**
     * Create a cache manger instance.
     *
     * @param \Psr\Container\ContainerInterface $container
     *
     * @return \Viserio\Component\Contract\Cache\Manager
     */
    public static function createCacheManager(ContainerInterface $container): CacheManagerContract
    {
        $cache = new CacheManager($container->get('config'));
        $cache->setContainer($container);

        return $cache;
    }
}


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

use Mockery;
use Mockery\MockInterface;
use Psr\Container\ContainerInterface;
use Viserio\Component\Contract\StaticalProxy\Exception\BadMethodCallException;
use Viserio\Component\Contract\StaticalProxy\Exception\RuntimeException;

class StaticalProxy
{
    /**
     * @var \Psr\Container\ContainerInterface the Container that provides the Proxy Subjects
     */
    protected static $container;

    /**
     * The resolved object instances.
     *
     * @var array
     */
    protected static $resolvedInstance = [];

    /**
     * Handle dynamic, static calls to the object.
     *
     * @param string $method
     * @param array  $args
     *
     * @throws \Viserio\Component\Contract\StaticalProxy\Exception\RuntimeException
     *
     * @return mixed
     */
    public static function __callStatic(string $method, array $args)
    {
        $instance = static::getStaticalProxyRoot();

        if (! $instance) {
            throw new RuntimeException('A statical proxy root has not been set.');
        }

        return $instance->{$method}(...$args);
    }

    /**
     * Sets the Container that will be used to retrieve the Proxy Subject.
     *
     * @param \Psr\Container\ContainerInterface $container The Container that provides the real Proxy Subject
     */
    public static function setContainer(ContainerInterface $container): void
    {
        static::$container = $container;
    }

    /**
     * Retrieves the instance of the Proxy Subject from the Container that the Static Proxy is associated with.
     *
     * @return mixed
     */
    public static function getInstance()
    {
        return static::$container->get(static::getInstanceIdentifier());
    }

    /**
     * Retrieves the Instance Identifier that is used to retrieve the Proxy Subject from the Container.
     *
     * @throws \Viserio\Component\Contract\StaticalProxy\Exception\BadMethodCallException if the method has not been implemented by a subclass
     *
     * @return object|string
     */
    public static function getInstanceIdentifier()
    {
        throw new BadMethodCallException(\sprintf('The [%s] method must be implemented by a subclass.', __METHOD__));
    }

    /**
     * Hot swap the underlying instance behind the static proxy.
     *
     * @param mixed $instance
     */
    public static function swap($instance): void
    {
        static::$resolvedInstance[static::getInstanceIdentifier()] = $instance;
    }

    /**
     * Initiate a mock expectation on the static proxy.
     *
     * @return \Mockery\ExpectationInterface|\Mockery\HigherOrderMessage
     */
    public static function shouldReceive()
    {
        $name = static::getInstanceIdentifier();

        if (static::isMock()) {
            $mock = static::$resolvedInstance[$name];
        } else {
            $mock = static::createFreshMockInstance($name);
        }

        return $mock->shouldReceive(...\func_get_args());
    }

    /**
     * Get the root object behind the static proxy.
     *
     * @return null|object
     */
    public static function getStaticalProxyRoot(): ?object
    {
        return static::resolveStaticalProxyInstance(static::getInstanceIdentifier());
    }

    /**
     * Clear a resolved static proxy instance.
     *
     * @param string $name
     */
    public static function clearResolvedInstance(string $name): void
    {
        unset(static::$resolvedInstance[$name]);
    }

    /**
     * Clear all of the resolved instances.
     */
    public static function clearResolvedInstances(): void
    {
        static::$resolvedInstance = [];
    }

    /**
     * Resolve the statical proxy root instance from the app.
     *
     * @param object|string $name
     *
     * @return object
     */
    protected static function resolveStaticalProxyInstance($name): object
    {
        if (\is_object($name)) {
            return $name;
        }

        if (isset(static::$resolvedInstance[$name])) {
            return static::$resolvedInstance[$name];
        }

        return static::$resolvedInstance[$name] = static::$container->get($name);
    }

    /**
     * Create a fresh mock instance for the given class.
     *
     * @param string $name
     *
     * @return \Mockery\MockInterface
     */
    protected static function createFreshMockInstance(string $name): MockInterface
    {
        static::$resolvedInstance[$name] = $mock = static::createMock();

        $mock->shouldAllowMockingProtectedMethods();

        return $mock;
    }

    /**
     * Create a fresh mock instance for the given class.
     *
     * @return \Mockery\MockInterface
     */
    protected static function createMock(): MockInterface
    {
        if ($root = static::getStaticalProxyRoot()) {
            return Mockery::mock(\get_class($root));
        }
        // @codeCoverageIgnoreStart
        return Mockery::mock();
        // @codeCoverageIgnoreStop
    }

    /**
     * Determines whether a mock is set as the instance of the static proxy.
     *
     * @return bool
     */
    protected static function isMock(): bool
    {
        $name = static::getInstanceIdentifier();

        return isset(static::$resolvedInstance[$name]) &&
                static::$resolvedInstance[$name] instanceof MockInterface;
    }
}


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

use Fig\Http\Message\StatusCodeInterface;
use Narrowspark\HttpStatus\HttpStatus;
use Psr\Http\Message\ResponseInterface;
use Viserio\Component\Contract\Http\Exception\InvalidArgumentException;

class Response extends AbstractMessage implements ResponseInterface, StatusCodeInterface
{
    /**
     * @var string
     */
    private $reasonPhrase = '';

    /**
     * @var null|int
     */
    private $statusCode;

    /**
     * Create a new response instance.
     *
     * @param int                                                    $status  status code for the response, if any
     * @param array                                                  $headers headers for the response, if any
     * @param null|\Psr\Http\Message\StreamInterface|resource|string $body    Stream identifier and/or actual stream resource
     * @param string                                                 $version protocol version
     *
     * @throws \Narrowspark\HttpStatus\Exception\InvalidArgumentException          on any invalid status
     * @throws \Viserio\Component\Contract\Http\Exception\InvalidArgumentException on any invalid content
     */
    public function __construct(
        int $status     = self::STATUS_OK,
        array $headers  = [],
        $body           = null,
        string $version = '1.1'
    ) {
        $this->statusCode = HttpStatus::filterStatusCode($status);

        if ($body !== '' && $body !== null) {
            $this->stream = Util::createStreamFor($body);
        }

        $this->setHeaders($headers);
        $this->protocol = $version;
    }

    /**
     * {@inheritdoc}
     */
    public function getReasonPhrase(): string
    {
        if ($this->reasonPhrase === '') {
            $this->reasonPhrase = HttpStatus::getReasonPhrase($this->statusCode);
        }

        return $this->reasonPhrase;
    }

    /**
     * {@inheritdoc}
     */
    public function getStatusCode(): int
    {
        return $this->statusCode;
    }

    /**
     * {@inheritdoc}
     */
    public function withStatus($code, $reasonPhrase = ''): ResponseInterface
    {
        if (! \is_numeric($code)
            || \is_float($code)
            || $code < HttpStatus::MINIMUM
            || $code > HttpStatus::MAXIMUM
        ) {
            throw new InvalidArgumentException(\sprintf(
                'Invalid status code [%s]; must be an integer between %d and %d, inclusive.',
                \is_scalar($code) ? $code : \gettype($code),
                HttpStatus::MINIMUM,
                HttpStatus::MAXIMUM
            ));
        }

        if (! \is_string($reasonPhrase)) {
            throw new InvalidArgumentException(\sprintf(
                'Unsupported response reason phrase; must be a string, received [%s].',
                \is_object($reasonPhrase) ? \get_class($reasonPhrase) : \gettype($reasonPhrase)
            ));
        }

        $new               = clone $this;
        $new->statusCode   = HttpStatus::filterStatusCode($code);
        $new->reasonPhrase = $reasonPhrase;

        return $new;
    }
}


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

use Closure;
use InvalidArgumentException;
use Psr\Container\ContainerInterface;
use Viserio\Component\Contract\Bus\Dispatcher as DispatcherContract;
use Viserio\Component\Contract\Container\Traits\ContainerAwareTrait;
use Viserio\Component\Pipeline\Pipeline;
use Viserio\Component\Support\Traits\InvokerAwareTrait;

class Dispatcher implements DispatcherContract
{
    use ContainerAwareTrait;
    use InvokerAwareTrait;

    /**
     * The pipeline instance.
     *
     * @var \Viserio\Component\Pipeline\Pipeline
     */
    protected $pipeline;

    /**
     * The pipes to send commands through before dispatching.
     *
     * @var array
     */
    protected $pipes = [];

    /**
     * All of the command-to-handler mappings.
     *
     * @var array
     */
    protected $mappings = [];

    /**
     * The method to call on handler.
     *
     * @var string
     */
    protected $method = 'handle';

    /**
     * The fallback mapping Closure.
     *
     * @var null|\Closure
     */
    protected $mapper;

    /**
     * Create a new command dispatcher instance.
     *
     * @param \Psr\Container\ContainerInterface $container
     */
    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;

        $pipeline = new Pipeline();
        $pipeline->setContainer($container);

        $this->pipeline = $pipeline;
    }

    /**
     * {@inheritdoc}
     */
    public function via(string $method): DispatcherContract
    {
        $this->method = $method;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function resolveHandler($command)
    {
        if (\method_exists($command, $this->method)) {
            return $command;
        }

        return $this->container->get($this->getHandlerClass($command));
    }

    /**
     * {@inheritdoc}
     */
    public function getHandlerClass($command): string
    {
        if (\method_exists($command, $this->method)) {
            return \get_class($command);
        }

        return $this->inflectSegment($command, 0);
    }

    /**
     * {@inheritdoc}
     */
    public function getHandlerMethod($command): string
    {
        if (\method_exists($command, $this->method)) {
            return $this->method;
        }

        return $this->inflectSegment($command, 1);
    }

    /**
     * {@inheritdoc}
     */
    public function maps(array $commands): void
    {
        $this->mappings = \array_merge($this->mappings, $commands);
    }

    /**
     * {@inheritdoc}
     */
    public function mapUsing(Closure $mapper): void
    {
        $this->mapper = $mapper;
    }

    /**
     * {@inheritdoc}
     */
    public function dispatch($command, Closure $afterResolving = null)
    {
        return $this->pipeline->send($command)->through($this->pipes)->then(function ($command) use ($afterResolving) {
            if (\method_exists($command, $this->method)) {
                return $this->getInvoker()->call([$command, $this->method]);
            }

            $handler = $this->resolveHandler($command);

            if ($afterResolving !== null) {
                $afterResolving($handler);
            }

            return $handler->{$this->getHandlerMethod($command)}($command);
        });
    }

    /**
     * {@inheritdoc}
     */
    public function pipeThrough(array $pipes): DispatcherContract
    {
        $this->pipes = $pipes;

        return $this;
    }

    /**
     * Get the given handler segment for the given command.
     *
     * @param mixed $command
     * @param int   $segment
     *
     * @throws \InvalidArgumentException
     *
     * @return string
     */
    protected function inflectSegment($command, int $segment): string
    {
        $className = \get_class($command);

        // Get the given segment from a given class handler.
        if (isset($this->mappings[$className])) {
            return \explode('@', $this->mappings[$className])[$segment];
        }

        // Get the given segment from a given class handler using the custom mapper.
        if (\is_callable($this->mapper)) {
            return \explode('@', \call_user_func($this->mapper, [$command]))[$segment];
        }

        throw new InvalidArgumentException(\sprintf('No handler registered for command [%s].', $className));
    }
}


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

use DateTime;
use DateTimeImmutable;
use Narrowspark\Http\Message\Util\InteractsWithDisposition;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
use SplFileInfo;
use Viserio\Component\Contract\Http\Exception\FileException;
use Viserio\Component\Contract\Http\Exception\InvalidArgumentException;
use Viserio\Component\Contract\Http\Exception\LogicException;
use Viserio\Component\Http\AbstractMessage;
use Viserio\Component\Http\File\File;
use Viserio\Component\Http\Response;
use Viserio\Component\Http\Stream;
use Viserio\Component\Http\Util;

class BinaryFileResponse extends Response
{
    /**
     * @var string
     */
    public const DISPOSITION_ATTACHMENT = 'attachment';

    /**
     * @var string
     */
    public const DISPOSITION_INLINE = 'inline';

    /**
     * Should the file be deleted after send.
     *
     * @var bool
     */
    protected $deleteFileAfterSend = false;

    /**
     * A Viserio Http File instance.
     *
     * @var \Viserio\Component\Http\File\File
     */
    protected $file;

    /**
     * @param \SplFileInfo|string|\Viserio\Component\Http\File\File $file               The file to stream
     *                                                                                is semantically equivalent to $filename. If the filename is already ASCII,
     *                                                                                it can be omitted, or just copied from $filename
     * @param int                                                   $status             The response status code
     * @param array                                                 $headers            An array of response headers
     * @param null|string                                           $contentDisposition The type of Content-Disposition to set automatically with the filename
     * @param bool                                                  $autoETag           Whether the ETag header should be automatically set
     * @param bool                                                  $autoLastModified   Whether the Last-Modified header should be automatically set
     *
     * @throws \Viserio\Component\Contract\Http\Exception\FileNotFoundException
     * @throws \Viserio\Component\Contract\Http\Exception\InvalidArgumentException
     * @throws \Viserio\Component\Contract\Http\Exception\FileException
     */
    public function __construct(
        $file,
        int $status                = self::STATUS_OK,
        array $headers             = [],
        string $contentDisposition = null,
        bool $autoETag             = false,
        bool $autoLastModified     = true
    ) {
        parent::__construct($status, $headers, null);

        $this->setFile($file, $contentDisposition, $autoETag, $autoLastModified);
    }

    /**
     * If this is set to true, the file will be unlinked after the request is send
     * Note: If the X-Sendfile header is used, the deleteFileAfterSend setting will not be used.
     *
     * @param bool $shouldDelete
     *
     * @return $this
     */
    public function deleteFileAfterSend(bool $shouldDelete = true): self
    {
        $this->deleteFileAfterSend = $shouldDelete;

        return $this;
    }

    /**
     * Gets the file.
     *
     * @return \Viserio\Component\Http\File\File
     */
    public function getFile(): File
    {
        return $this->file;
    }

    /**
     * Transform a SplFileInfo to a Http File and check if the file exists.
     *
     * @param \SplFileInfo|string|\Viserio\Component\Http\File\File $file
     * @param string                                                $contentDisposition
     * @param bool                                                  $autoETag
     * @param bool                                                  $autoLastModified
     *
     * @throws \Viserio\Component\Contract\Http\Exception\FileNotFoundException
     * @throws \Viserio\Component\Contract\Http\Exception\InvalidArgumentException
     * @throws \Viserio\Component\Contract\Http\Exception\FileException
     *
     * @return \Psr\Http\Message\ResponseInterface
     */
    public function setFile(
        $file,
        string $contentDisposition = null,
        bool $autoETag             = false,
        bool $autoLastModified     = true
    ): ResponseInterface {
        if (! $file instanceof File) {
            if ($file instanceof SplFileInfo) {
                $file = new File($file->getPathname());
            } elseif (\is_string($file)) {
                $file = new File($file);
            } else {
                throw new InvalidArgumentException(\sprintf(
                    'Invalid content [%s] provided to %s.',
                    (\is_object($file) ? \get_class($file) : \gettype($file)),
                    __CLASS__
                ));
            }
        }

        if (! $file->isReadable()) {
            throw new FileException('File must be readable.');
        }

        $this->file = $file;

        if ($autoETag === true) {
            $this->setAutoEtag();
        }

        if ($autoLastModified === true) {
            $this->setAutoLastModified();
        }

        if ($contentDisposition) {
            $this->headers['Content-Length']      = [$this->file->getSize()];
            $this->headers['Content-Disposition'] = [
                InteractsWithDisposition::makeDisposition(
                    $contentDisposition,
                    $this->file->getFilename(),
                    InteractsWithDisposition::encodedFallbackFilename($this->file->getFilename())
                ),
            ];

            $this->headerNames['content-length']      = 'Content-Length';
            $this->headerNames['content-disposition'] = 'Content-Disposition';

            if (! $this->hasHeader('Content-Type')) {
                $this->headers['Content-Type']     = [$this->file->getMimeType() ?? 'application/octet-stream'];
                $this->headerNames['content-type'] = 'Content-Type';
            }
        }

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function withBody(StreamInterface $body): AbstractMessage
    {
        throw new LogicException('The content cannot be set on a BinaryFileResponse instance.');
    }

    /**
     * {@inheritdoc}
     */
    public function getBody(): StreamInterface
    {
        $fileStream = new Stream(\fopen($this->file->getPathname(), 'rb'));
        $outStream  = new Stream(\fopen('php://output', 'wb'));

        Util::copyToStream($fileStream, $outStream);

        $fileStream->close();

        if ($this->deleteFileAfterSend) {
            \unlink($this->file->getPathname());
        }

        return $outStream;
    }

    /**
     * Sets the Content-Disposition header with the given filename.
     *
     * @param string $disposition      ResponseHeaderBag::DISPOSITION_INLINE or ResponseHeaderBag::DISPOSITION_ATTACHMENT
     * @param string $filename         Optionally use this UTF-8 encoded filename instead of the real name of the file
     * @param string $filenameFallback A fallback filename, containing only ASCII characters. Defaults to an automatically encoded filename
     *
     * @throws \InvalidArgumentException
     *
     * @return \Psr\Http\Message\ResponseInterface
     */
    public function setContentDisposition(
        string $disposition,
        string $filename         = '',
        string $filenameFallback = ''
    ): ResponseInterface {
        if ($filenameFallback === '') {
            $filenameFallback = InteractsWithDisposition::encodedFallbackFilename($filename);
        }

        return InteractsWithDisposition::appendDispositionHeader($this, $disposition, $filename, $filenameFallback);
    }

    /**
     * Automatically sets the Last-Modified header according the file modification date.
     *
     * @return void
     */
    protected function setAutoLastModified(): void
    {
        $date = DateTime::createFromFormat('U', (string) $this->file->getMTime());
        $date = DateTimeImmutable::createFromMutable($date);
        $date = $date->setTimezone(new \DateTimeZone('UTC'));

        $this->headers['Last-Modified']     = [$date->format('D, d M Y H:i:s') . ' GMT'];
        $this->headerNames['last-modified'] = 'Last-Modified';
    }

    /**
     * Automatically sets the ETag header according to the checksum of the file.
     *
     * @return void
     */
    protected function setAutoEtag(): void
    {
        $eTag = \base64_encode(\hash_file('sha256', $this->file->getPathname(), true));

        if (\strpos($eTag, '"') !== 0) {
            $eTag = '"' . $eTag . '"';
        }

        $this->headers['Etag']     = [$eTag];
        $this->headerNames['etag'] = 'Etag';
    }
}


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

use Interop\Container\ServiceProviderInterface;
use Psr\Container\ContainerInterface;
use Viserio\Component\Contract\Foundation\Kernel as KernelContract;
use Viserio\Component\Contract\OptionsResolver\ProvidesDefaultOptions as ProvidesDefaultOptionsContract;
use Viserio\Component\Contract\OptionsResolver\RequiresComponentConfig as RequiresComponentConfigContract;
use Viserio\Component\Contract\Profiler\Profiler as ProfilerContract;
use Viserio\Component\Contract\Routing\Router as RouterContract;
use Viserio\Component\HttpFoundation\DataCollector\ViserioHttpDataCollector;
use Viserio\Component\OptionsResolver\Traits\OptionsResolverTrait;

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

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

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

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

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

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

            if ($options['collector']['viserio_http']) {
                $profiler->addCollector(
                    new ViserioHttpDataCollector(
                        $container->get(RouterContract::class),
                        $kernel->getRoutesPath()
                    ),
                    1
                );
            }
        }

        return $profiler;
    }
}


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

use Psr\Http\Message\StreamInterface;
use Viserio\Component\Contract\Http\Exception\InvalidArgumentException;
use Viserio\Component\Http\Response;
use Viserio\Component\Http\Response\Traits\InjectContentTypeTrait;
use Viserio\Component\Http\Stream;

class XmlResponse extends Response
{
    use InjectContentTypeTrait;

    /**
     * Create an XML response.
     *
     * Produces an XML response with a Content-Type of text/xml and a default status of 200.
     *
     * @param \Psr\Http\Message\StreamInterface|string $xml     plain text or stream for the message body
     * @param null|string                              $charset content charset; default is utf-8
     * @param int                                      $status  integer status code for the response; 200 by default
     * @param array                                    $headers array of headers to use at initialization
     * @param string                                   $version protocol version
     *
     * @throws \Viserio\Component\Contract\Http\Exception\InvalidArgumentException if $xml is neither a string or stream
     */
    public function __construct(
        $xml,
        ?string $charset = null,
        int $status      = self::STATUS_OK,
        array $headers   = [],
        string $version  = '1.1'
    ) {
        parent::__construct(
            $status,
            $this->injectContentType('text/xml; charset=' . ($charset ?? 'utf-8'), $headers),
            $this->createBody($xml),
            $version
        );
    }

    /**
     * Create the message body.
     *
     * @param \Psr\Http\Message\StreamInterface|string $text
     *
     * @throws \Viserio\Component\Contract\Http\Exception\InvalidArgumentException if $text is neither a string or stream
     *
     * @return \Psr\Http\Message\StreamInterface
     */
    private function createBody($text): StreamInterface
    {
        if ($text instanceof StreamInterface) {
            return $text;
        }

        if (! \is_string($text)) {
            throw new InvalidArgumentException(\sprintf(
                'Invalid content [%s] provided to %s',
                (\is_object($text) ? \get_class($text) : \gettype($text)),
                __CLASS__
            ));
        }

        $body = new Stream(\fopen('php://temp', 'w+b'));
        $body->write($text);
        $body->rewind();

        return $body;
    }
}


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

use Viserio\Component\Contract\Support\Stringable as StringableContract;
use Viserio\Component\Cookie\Traits\CookieValidatorTrait;

final class Cookie implements StringableContract
{
    use CookieValidatorTrait;

    /**
     * @var string
     */
    private $name;

    /**
     * @var null|string
     */
    private $value;

    /**
     * Create a new cookie instance.
     *
     * @param string      $name  the name of the cookie
     * @param null|string $value the value of the cookie
     *
     * @throws \InvalidArgumentException
     */
    public function __construct(string $name, ?string $value = null)
    {
        $this->validateName($name);
        $this->validateValue($value);

        $this->name  = $name;
        $this->value = $value;
    }

    /**
     * Returns the cookie as a string.
     *
     * @return string The cookie
     */
    public function __toString(): string
    {
        $name = \urlencode($this->name) . '=';

        return $name . \urlencode($this->getValue());
    }

    /**
     * Returns the name.
     *
     * @return string
     */
    public function getName(): string
    {
        return $this->name;
    }

    /**
     * Returns the value.
     *
     * @return string
     */
    public function getValue(): string
    {
        return (string) $this->value;
    }

    /**
     * Sets the value.
     *
     * @param null|string $value
     *
     * @return $this
     */
    public function withValue(?string $value = null): Cookie
    {
        $this->validateValue($value);

        $new        = clone $this;
        $new->value = $value;

        return $new;
    }
}


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

use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Viserio\Component\Contract\Profiler\Profiler as ProfilerContract;

class ProfilerMiddleware implements MiddlewareInterface
{
    /**
     * The Profiler instance.
     *
     * @var null|\Viserio\Component\Contract\Profiler\Profiler
     */
    protected $profiler;

    /**
     * Create a new middleware instance.
     *
     * @param \Psr\Container\ContainerInterface $container
     */
    public function __construct(ContainerInterface $container)
    {
        $profiler = null;

        if ($container->has(ProfilerContract::class)) {
            $profiler = $container->get(ProfilerContract::class);
        }

        $this->profiler = $profiler;
    }

    /**
     * {@inheritdoc}
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $server    = $request->getServerParams();
        $startTime = $server['request_time_float'] ?? \microtime(true);

        $response = $handler->handle($request);

        if ($this->profiler === null) {
            return $response;
        }

        $response = $response->withHeader('x-response-time', \sprintf('%2.3fms', (\microtime(true) - $startTime) * 1000));

        // Modify the response to add the Profiler
        return $this->profiler->modifyResponse($request, $response);
    }
}


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

use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\UploadedFileInterface;
use Viserio\Component\Contract\Http\Exception\InvalidArgumentException;

class ServerRequest extends Request implements ServerRequestInterface
{
    /**
     * The request attributes.
     *
     * @var array
     */
    private $attributes = [];

    /**
     * The request cookies params.
     *
     * @var array
     */
    private $cookieParams = [];

    /**
     * @var null|array|object
     */
    private $parsedBody;

    /**
     * The request query string params.
     *
     * @var array
     */
    private $queryParams = [];

    /**
     * The server environment variables at the time the request was created.
     *
     * @var array
     */
    private $serverParams;

    /**
     * List of uploaded files.
     *
     * @var \Psr\Http\Message\UploadedFileInterface[]
     */
    private $uploadedFiles = [];

    /**
     * Create a new server request instance.
     *
     * @param null|\Psr\Http\Message\UriInterface|string             $uri          uRI for the request
     * @param null|string                                            $method       hTTP method for the request
     * @param array                                                  $headers      headers for the message
     * @param null|\Psr\Http\Message\StreamInterface|resource|string $body         message body
     * @param string                                                 $version      hTTP protocol version
     * @param array                                                  $serverParams Typically the $_SERVER superglobal
     */
    public function __construct(
        $uri,
        $method             = 'GET',
        array $headers      = [],
        $body               = null,
        string $version     = '1.1',
        array $serverParams = []
    ) {
        $this->serverParams = $serverParams;

        parent::__construct($uri, $method, $headers, $body, $version);
    }

    /**
     * {@inheritdoc}
     */
    public function getAttributes(): ?array
    {
        return $this->attributes;
    }

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

    /**
     * {@inheritdoc}
     */
    public function getParsedBody()
    {
        return $this->parsedBody;
    }

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

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

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

    /**
     * {@inheritdoc}
     */
    public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface
    {
        $this->validateUploadedFiles($uploadedFiles);

        $new                = clone $this;
        $new->uploadedFiles = $uploadedFiles;

        return $new;
    }

    /**
     * {@inheritdoc}
     */
    public function withCookieParams(array $cookies): ServerRequestInterface
    {
        $new               = clone $this;
        $new->cookieParams = $cookies;

        return $new;
    }

    /**
     * {@inheritdoc}
     */
    public function withQueryParams(array $query): ServerRequestInterface
    {
        $new              = clone $this;
        $new->queryParams = $query;

        return $new;
    }

    /**
     * {@inheritdoc}
     */
    public function withParsedBody($data): ServerRequestInterface
    {
        if (! \is_array($data) && ! \is_object($data) && null !== $data) {
            throw new InvalidArgumentException(\sprintf(
                '%s expects a null, array, or object argument; received [%s]',
                __METHOD__,
                \gettype($data)
            ));
        }

        $new             = clone $this;
        $new->parsedBody = $data;

        return $new;
    }

    /**
     * {@inheritdoc}
     */
    public function getAttribute($attribute, $default = null)
    {
        if (! \array_key_exists($attribute, $this->attributes)) {
            return $default;
        }

        return $this->attributes[$attribute];
    }

    /**
     * {@inheritdoc}
     */
    public function withAttribute($attribute, $value): ServerRequestInterface
    {
        $new                         = clone $this;
        $new->attributes[$attribute] = $value;

        return $new;
    }

    /**
     * {@inheritdoc}
     */
    public function withoutAttribute($attribute): ServerRequestInterface
    {
        if (! \array_key_exists($attribute, $this->attributes)) {
            return $this;
        }

        $new = clone $this;
        unset($new->attributes[$attribute]);

        return $new;
    }

    /**
     * Recursively validate the structure in an uploaded files array.
     *
     * @param array $uploadedFiles
     *
     * @throws \Viserio\Component\Contract\Http\Exception\InvalidArgumentException if any leaf is not an UploadedFileInterface instance
     */
    private function validateUploadedFiles(array $uploadedFiles): void
    {
        foreach ($uploadedFiles as $file) {
            if (\is_array($file)) {
                $this->validateUploadedFiles($file);

                continue;
            }

            if (! $file instanceof UploadedFileInterface) {
                throw new InvalidArgumentException('Invalid leaf in uploaded files structure.');
            }
        }
    }
}