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

use Monolog\Formatter\LineFormatter;
use Monolog\Handler\ErrorLogHandler;
use Monolog\Handler\HandlerInterface;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Handler\SlackWebhookHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\SyslogHandler;
use Monolog\Logger as Monolog;
use Psr\Log\LoggerInterface;
use Psr\Log\LoggerTrait;
use Viserio\Component\Contract\Events\Traits\EventManagerAwareTrait;
use Viserio\Component\Contract\Log\Exception\InvalidArgumentException;
use Viserio\Component\Contract\Log\Exception\RuntimeException;
use Viserio\Component\Contract\Manager\Exception\InvalidArgumentException as ManagerInvalidArgumentException;
use Viserio\Component\Contract\OptionsResolver\ProvidesDefaultOptions as ProvidesDefaultOptionsContract;
use Viserio\Component\Log\Traits\ParseLevelTrait;
use Viserio\Component\Manager\AbstractManager;

class LogManager extends AbstractManager implements
    LoggerInterface,
    ProvidesDefaultOptionsContract
{
    use LoggerTrait;
    use ParseLevelTrait;
    use EventManagerAwareTrait;

    /**
     * {@inheritdoc}
     */
    protected const CONFIG_LIST_NAME = 'channels';

    /**
     * Array of monolog processor callbacks.
     *
     * @var callable[]
     */
    private $processors = [];

    /**
     * {@inheritdoc}
     */
    public static function getDefaultOptions(): array
    {
        return [
            'default'   => 'single',
            'name'      => 'narrowspark',
            'channels'  => [
                'aggregate' => [
                    'driver'   => 'aggregate',
                    'channels' => ['single', 'daily'],
                ],
                'single' => [
                    'driver' => 'single',
                    'level'  => 'debug',
                ],
                'daily' => [
                    'driver' => 'daily',
                    'level'  => 'debug',
                    'days'   => 14,
                ],
                'syslog' => [
                    'driver' => 'syslog',
                    'level'  => 'debug',
                ],
                'errorlog' => [
                    'driver' => 'errorlog',
                    'level'  => 'debug',
                ],
                'slack' => [
                    'driver'   => 'slack',
                    'url'      => '',
                    'username' => null,
                    'emoji'    => ':boom:',
                    'level'    => 'critical',
                ],
            ],
        ];
    }

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

    /**
     * Adds a processor on to the stack.
     *
     * @param callable $callback
     *
     * @return $this
     */
    public function pushProcessor(callable $callback): self
    {
        \array_unshift($this->processors, $callback);

        return $this;
    }

    /**
     * Get a log channel instance.
     *
     * @param null|string $channel
     *
     * @return mixed
     */
    public function getChannel(?string $channel = null)
    {
        return $this->getDriver($channel);
    }

    /**
     * {@inheritdoc}
     */
    public function log($level, $message, array $context = []): void
    {
        $this->getDriver()->{$level}($message, $context);
    }

    /**
     * {@inheritdoc}
     */
    public function createDriver(array $config): LoggerInterface
    {
        try {
            $driver = parent::createDriver($config);
        } catch (ManagerInvalidArgumentException $exception) {
            $driver = $this->createEmergencyDriver();
            $driver->emergency(
                'Unable to create configured logger. Using emergency logger.',
                ['exception' => $exception]
            );
        }

        if ($driver instanceof Monolog) {
            $driver = new Logger($this->pushProcessorsToMonolog($config, $driver));
        }

        if ($this->eventManager !== null && \method_exists($driver, 'setEventManager')) {
            $driver->setEventManager($this->eventManager);
        }

        return $driver;
    }

    /**
     * Create a new, on-demand aggregate logger instance.
     *
     * @param array $config
     *
     * @return \Psr\Log\LoggerInterface
     */
    protected function createStackDriver(array $config): LoggerInterface
    {
        return $this->createAggregateDriver(['channels' => $config['channels'], 'channel' => $config['channel'] ?? null]);
    }

    /**
     * Create a aggregate log driver instance.
     *
     * @param array $config
     *
     * @return \Psr\Log\LoggerInterface
     */
    protected function createAggregateDriver(array $config): LoggerInterface
    {
        $handlers = [];

        foreach ((array) $config['channels'] as $channel) {
            foreach ($this->getDriver($channel)->getHandlers() as $handler) {
                $handlers[] = $handler;
            }
        }

        return new Monolog($this->parseChannel($config), $handlers);
    }

    /**
     * Create an emergency log handler to avoid white screens of death.
     *
     * @throws \Exception
     * @throws \InvalidArgumentException
     *
     * @return \Psr\Log\LoggerInterface
     */
    protected function createEmergencyDriver(): LoggerInterface
    {
        $handler = new StreamHandler(
            $this->getFilePath(),
            self::parseLevel('debug'),
            $config['bubble'] ?? true,
            $config['permission'] ?? null,
            $config['locking'] ?? false
        );
        $handler->setFormatter($this->getConfiguredLineFormatter());

        return new Monolog($this->resolvedOptions['name'], [$handler]);
    }

    /**
     * Create an instance of the single file log driver.
     *
     * @param array $config
     *
     * @throws \Exception
     * @throws \InvalidArgumentException
     *
     * @return \Psr\Log\LoggerInterface
     */
    protected function createSingleDriver(array $config): LoggerInterface
    {
        $handler = new StreamHandler(
            $this->getFilePath(),
            self::parseLevel($config['level'] ?? 'debug'),
            $config['bubble'] ?? true,
            $config['permission'] ?? null,
            $config['locking'] ?? false
        );
        $handler->setFormatter($this->getConfiguredLineFormatter());

        return new Monolog($this->parseChannel($config), [$handler]);
    }

    /**
     * Create an instance of the daily file log driver.
     *
     * @param array $config
     *
     * @throws \InvalidArgumentException
     *
     * @return \Psr\Log\LoggerInterface
     */
    protected function createDailyDriver(array $config): LoggerInterface
    {
        $handler = new RotatingFileHandler(
            $this->getFilePath(),
            $config['days'] ?? 7,
            self::parseLevel($config['level'] ?? 'debug'),
            $config['bubble'] ?? true,
            $config['permission'] ?? null,
            $config['locking'] ?? false
        );
        $handler->setFormatter($this->getConfiguredLineFormatter());

        return new Monolog($this->parseChannel($config), [$handler]);
    }

    /**
     * Create an instance of the syslog log driver.
     *
     * @param array $config
     *
     * @throws \InvalidArgumentException
     *
     * @return \Psr\Log\LoggerInterface
     */
    protected function createSyslogDriver(array $config): LoggerInterface
    {
        $handler = new SyslogHandler(
            $config['name'],
            $config['facility'] ?? \LOG_USER,
            self::parseLevel($config['level'] ?? 'debug')
        );
        $handler->setFormatter($this->getConfiguredLineFormatter());

        return new Monolog($this->parseChannel($config), [$handler]);
    }

    /**
     * Create an instance of the "error log" log driver.
     *
     * @param array $config
     *
     * @throws \InvalidArgumentException
     *
     * @return \Psr\Log\LoggerInterface
     */
    protected function createErrorlogDriver(array $config): LoggerInterface
    {
        $handler = new ErrorLogHandler(
            $config['type'] ?? ErrorLogHandler::OPERATING_SYSTEM,
            self::parseLevel($config['level'] ?? 'debug')
        );
        $handler->setFormatter($this->getConfiguredLineFormatter());

        return new Monolog($this->parseChannel($config), [$handler]);
    }

    /**
     * Create an instance of the Slack log driver.
     *
     * @param array $config
     *
     * @throws \InvalidArgumentException
     *
     * @return \Psr\Log\LoggerInterface
     */
    protected function createSlackDriver(array $config): LoggerInterface
    {
        $handler = new SlackWebhookHandler(
            $config['url'],
            $config['channel'] ?? null,
            $config['username'] ?? $this->resolvedOptions['name'],
            $config['attachment'] ?? true,
            $config['emoji'] ?? ':boom:',
            $config['short'] ?? false,
            $config['context'] ?? true,
            self::parseLevel($config['level'] ?? 'debug'),
            $config['bubble'] ?? true,
            $config['exclude_fields'] ?? []
        );
        $handler->setFormatter($this->getConfiguredLineFormatter());

        return new Monolog($this->parseChannel($config), [$handler]);
    }

    /**
     * Create a custom log driver instance.
     *
     * @param array $config
     *
     * @throws \Viserio\Component\Contract\Log\Exception\RuntimeException
     *
     * @return \Psr\Log\LoggerInterface
     */
    protected function createCustomDriver(array $config): LoggerInterface
    {
        $via            = $config['via'];
        $config['name'] = $config['original_name'];

        unset($config['original_name']);

        if (\is_callable($via)) {
            return $via($config);
        }

        if ($this->container !== null && $this->container->has($via)) {
            return $this->container->get($via);
        }

        throw new RuntimeException(\sprintf(
            'Given custom logger [%s] could not be resolved.',
            $config['name']
        ));
    }

    /**
     * Create an instance of any handler available in Monolog.
     *
     * @param array $config
     *
     * @throws \InvalidArgumentException
     *
     * @return \Psr\Log\LoggerInterface
     */
    protected function createMonologDriver(array $config): LoggerInterface
    {
        if ($this->container === null) {
            throw new RuntimeException('No container instance was found.');
        }

        $config['name'] = $config['original_name'];

        unset($config['original_name']);

        if ($this->container->has($config['handler'])) {
            $handler = $this->container->get($config['handler']);

            if (! \is_a($handler, HandlerInterface::class, true)) {
                throw new InvalidArgumentException(\sprintf('[%s] must be an instance of [%s]', $config['handler'], HandlerInterface::class));
            }
        } else {
            throw new InvalidArgumentException(\sprintf('Handler [%s] is not managed by the container.', $config['handler']));
        }

        if (! isset($config['formatter'])) {
            $handler->setFormatter($this->getConfiguredLineFormatter());
        } elseif ($config['formatter'] !== 'default') {
            $handler->setFormatter($this->container->get($config['formatter']));
        }

        $monolog = new Monolog($this->parseChannel($config));

        $monolog->pushHandler($handler);

        return $monolog;
    }

    /**
     * Returns a line formatter with included stacktraces.
     *
     * @return \Monolog\Formatter\LineFormatter
     */
    protected function getConfiguredLineFormatter(): LineFormatter
    {
        $formatter = new LineFormatter(
            null,
            null,
            true,
            true
        );

        $formatter->includeStacktraces();

        return $formatter;
    }

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

        if (isset($config['driver']) && \in_array($config['driver'], ['custom', 'monolog'], true)) {
            $config['original_name'] = $config['name'];
            $config['name']          = $config['driver'];
        }

        return $config;
    }

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

    /**
     * Push given processors to monolog.
     *
     * @param array           $config
     * @param \Monolog\Logger $driver
     *
     * @return \Monolog\Logger
     */
    protected function pushProcessorsToMonolog(array $config, Monolog $driver): Monolog
    {
        $processors = $this->processors;

        if (isset($config['processors'])) {
            $processors = \array_merge($processors, $config['processors']);
        }

        foreach ($processors as $processor) {
            $driver->pushProcessor($processor);
        }

        return $driver;
    }

    /**
     * Extract the log channel from the given configuration.
     *
     * @param array $config
     *
     * @return string
     */
    private function parseChannel(array $config): string
    {
        return $config['channel'] ?? $this->resolvedOptions['env'];
    }

    /**
     * Return the file path for some logger.
     *
     * @return string
     */
    private function getFilePath(): string
    {
        return $this->resolvedOptions['path'] . \DIRECTORY_SEPARATOR . $this->resolvedOptions['env'] . '.log';
    }
}


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

use Psr\Http\Message\ServerRequestInterface;
use Viserio\Component\Contract\Http\Exception\InvalidArgumentException;
use Viserio\Component\Contract\Http\Exception\UnexpectedValueException;
use Viserio\Component\Http\Stream\CachingStream;
use Viserio\Component\Http\Stream\LazyOpenStream;

final class ServerRequestBuilder
{
    /**
     * Create a new server request from the current environment variables.
     * Defaults to a GET request to minimise the risk of an \InvalidArgumentException.
     * Includes the current request headers as supplied by the server.
     * Defaults to php://input for the request body.
     *
     * @throws \InvalidArgumentException if no valid method or URI can be determined
     */
    public function createFromGlobals(): ServerRequestInterface
    {
        $server = $_SERVER;

        if (! isset($server['REQUEST_METHOD'])) {
            $server['REQUEST_METHOD'] = 'GET';
        }

        return $this->createFromArray(
            $server,
            Util::getAllHeaders($server),
            $_COOKIE,
            $_GET,
            $_POST,
            $_FILES,
            new CachingStream(new LazyOpenStream('php://input', 'r+'))
        );
    }

    /**
     * Create a new server request from a set of arrays.
     *
     * @param array                                                         $server  typically $_SERVER or similar structure
     * @param array                                                         $headers typically the output of getallheaders() or similar structure
     * @param array                                                         $cookie  typically $_COOKIE or similar structure
     * @param array                                                         $get     typically $_GET or similar structure
     * @param array                                                         $post    typically $_POST or similar structure
     * @param array                                                         $files   typically $_FILES or similar structure
     * @param null|\Psr\Http\Message\ServerRequestInterface|resource|string $body    Typically stdIn
     *
     * @throws \InvalidArgumentException if no valid method or URI can be determined
     *
     * @return \Psr\Http\Message\ServerRequestInterface
     */
    public function createFromArray(
        array $server,
        array $headers = [],
        array $cookie  = [],
        array $get     = [],
        array $post    = [],
        array $files   = [],
        $body          = null
    ): ServerRequestInterface {
        if (isset($server['SERVER_ADDR'])) {
            $server['SERVER_ADDR'] = \str_replace('Server IP: ', '', $server['SERVER_ADDR']);
        }

        $serverRequest = new ServerRequest(
            Uri::createFromServer($server),
            $this->getMethodFromServer($server),
            $headers,
            $body,
            $this->marshalProtocolVersion($server),
            $server
        );

        return $serverRequest
            ->withCookieParams($cookie)
            ->withQueryParams($get)
            ->withParsedBody($post)
            ->withUploadedFiles(Util::normalizeFiles($files));
    }

    /**
     * @param array $server
     *
     * @return string
     */
    private function getMethodFromServer(array $server): string
    {
        if (! isset($server['REQUEST_METHOD'])) {
            throw new InvalidArgumentException('Cannot determine HTTP method.');
        }

        return $server['REQUEST_METHOD'];
    }

    /**
     * Return HTTP protocol version (X.Y).
     *
     * @param array $server
     *
     * @throws \UnexpectedValueException
     *
     * @return string
     */
    private function marshalProtocolVersion(array $server): string
    {
        if (! isset($server['SERVER_PROTOCOL'])) {
            return '1.1';
        }

        if (! \preg_match('#^(HTTP/)?(?P<version>[1-9]\d*(?:\.\d)?)$#', $server['SERVER_PROTOCOL'], $matches)) {
            throw new UnexpectedValueException(\sprintf(
                'Unrecognized protocol version [%s].',
                $server['SERVER_PROTOCOL']
            ));
        }

        return $matches['version'];
    }
}


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

use Doctrine\Common\Inflector\Inflector;

class Pluralizer
{
    /**
     * Uncountable word forms.
     *
     * @var array
     */
    private static $uncountable = [
        'access',
        'accommodation',
        'audio',
        'bison',
        'business',
        'carbon',
        'cash',
        'chassis',
        'chess',
        'commerce',
        'compensation',
        'content',
        'coreopsis',
        'damage',
        'data',
        'deer',
        'education',
        'emoji',
        'energy',
        'entertainment',
        'equipment',
        'evidence',
        'failure',
        'feedback',
        'firmware',
        'fish',
        'furniture',
        'garbage',
        'gold',
        'hardware',
        'information',
        'jedi',
        'knowledge',
        'logic',
        'love',
        'metadata',
        'management',
        'money',
        'moose',
        'news',
        'nutrition',
        'offspring',
        'plankton',
        'pokemon',
        'police',
        'rain',
        'rice',
        'series',
        'sheep',
        'software',
        'speed',
        'species',
        'success',
        'swine',
        'time',
        'traffic',
        'usage',
        'wheat',
    ];

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

    /**
     * Returns a list of uncountable words.
     *
     * @return array
     */
    public static function getUncountable(): array
    {
        return self::$uncountable;
    }

    /**
     * Get the plural form of an English word.
     *
     * @param string $value
     * @param int    $count
     *
     * @return string
     */
    public static function plural(string $value, int $count = 2): string
    {
        if ($count === 1 || static::uncountable($value)) {
            return $value;
        }

        $plural = Inflector::pluralize($value);

        return static::matchCase($plural, $value);
    }

    /**
     * Get the singular form of an English word.
     *
     * @param string $value
     *
     * @return string
     */
    public static function singular(string $value): string
    {
        $singular = Inflector::singularize($value);

        return static::matchCase($singular, $value);
    }

    /**
     * Determine if the given value is uncountable.
     *
     * @param string $value
     *
     * @return bool
     */
    protected static function uncountable(string $value): bool
    {
        return \in_array(\mb_strtolower($value), static::$uncountable, true);
    }

    /**
     * Attempt to match the case on two strings.
     *
     * @param string $value
     * @param string $comparison
     *
     * @return string
     */
    protected static function matchCase(string $value, string $comparison): string
    {
        $functions = ['mb_strtolower', 'mb_strtoupper', 'ucfirst', 'ucwords'];

        foreach ($functions as $function) {
            if ($function($comparison) === $comparison) {
                return $function($value);
            }
        }

        return $value;
    }
}


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

use League\Flysystem\Adapter\Local;
use League\Flysystem\AdapterInterface;
use Viserio\Component\Contract\Filesystem\Connector as ConnectorContract;
use Viserio\Component\Contract\OptionsResolver\ProvidesDefaultOptions as ProvidesDefaultOptionsContract;
use Viserio\Component\Contract\OptionsResolver\RequiresConfig as RequiresConfigContract;
use Viserio\Component\Contract\OptionsResolver\RequiresMandatoryOptions as RequiresMandatoryOptionsContract;
use Viserio\Component\Contract\OptionsResolver\RequiresValidatedConfig as RequiresValidatedConfigContract;
use Viserio\Component\OptionsResolver\Traits\OptionsResolverTrait;

final class LocalConnector implements
    ConnectorContract,
    RequiresConfigContract,
    ProvidesDefaultOptionsContract,
    RequiresMandatoryOptionsContract,
    RequiresValidatedConfigContract
{
    use OptionsResolverTrait;

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

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

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

    /**
     * {@inheritdoc}
     */
    public static function getDefaultOptions(): array
    {
        return [
            'write_flags'   => \LOCK_EX,
            'link_handling' => Local::DISALLOW_LINKS,
            'permissions'   => [],
        ];
    }

    /**
     * {@inheritdoc}
     */
    public static function getOptionValidators(): array
    {
        return [
            'host'     => ['string'],
            'port'     => ['string', 'int'],
            'username' => ['string'],
            'password' => ['string'],
        ];
    }

    /**
     * Establish a connection.
     *
     * @throws \Viserio\Component\Contract\OptionsResolver\Exception\InvalidArgumentException On wrong configuration
     *
     * @return \League\Flysystem\Adapter\Local
     */
    public function connect(): AdapterInterface
    {
        return new Local(
            $this->resolvedOptions['path'],
            $this->resolvedOptions['write_flags'],
            $this->resolvedOptions['link_handling'],
            $this->resolvedOptions['permissions']
        );
    }
}


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

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

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

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

    /**
     * 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 parse(string $payload): array
    {
        $json = \json_decode(\trim($payload), true, $this->depth, $this->options);

        if (\json_last_error() !== \JSON_ERROR_NONE) {
            throw new ParseException([
                'message' => \json_last_error_msg() . '.',
                'code'    => \json_last_error(),
                'file'    => $payload,
            ]);
        }

        return $json;
    }
}


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

use Interop\Container\ServiceProviderInterface;
use Psr\Container\ContainerInterface;
use Symfony\Component\Console\Application as SymfonyConsole;
use Viserio\Component\Console\Application;
use Viserio\Component\Contract\Events\EventManager as EventManagerContract;

class ConsoleServiceProvider implements ServiceProviderInterface
{
    /**
     * {@inheritdoc}
     */
    public function getFactories(): array
    {
        return [
            Application::class    => [self::class, 'createCerebro'],
            SymfonyConsole::class => static function (ContainerInterface $container) {
                return $container->get(Application::class);
            },
            'console' => static function (ContainerInterface $container) {
                return $container->get(Application::class);
            },
            'cerebro' => static function (ContainerInterface $container) {
                return $container->get(Application::class);
            },
        ];
    }

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

    /**
     * Create a new console application instance.
     *
     * @param \Psr\Container\ContainerInterface $container
     *
     * @return \Viserio\Component\Console\Application
     */
    public static function createCerebro(ContainerInterface $container): Application
    {
        $console = new Application();
        $console->setContainer($container);

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

        return $console;
    }
}


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

use Monolog\Logger as Monolog;
use Psr\Log\InvalidArgumentException;
use Psr\Log\LoggerInterface as PsrLoggerInterface;
use Psr\Log\LoggerTrait;
use Psr\Log\LogLevel;
use Viserio\Component\Contract\Events\Traits\EventManagerAwareTrait;
use Viserio\Component\Contract\Support\Arrayable;
use Viserio\Component\Contract\Support\Jsonable;
use Viserio\Component\Log\Event\MessageLoggedEvent;
use Viserio\Component\Log\Traits\ParseLevelTrait;

class Logger extends LogLevel implements PsrLoggerInterface
{
    use ParseLevelTrait;
    use EventManagerAwareTrait;
    use LoggerTrait;

    /**
     * The MESSAGE event allows you building profilers or other tools
     * that aggregate all of the log messages for a given "request" cycle.
     *
     * @var string
     */
    public const MESSAGE = 'log.message';

    /**
     * The handler parser instance.
     *
     * @var \Monolog\Logger
     */
    protected $logger;

    /**
     * Create a new log writer instance.
     *
     * @param \Monolog\Logger $logger
     */
    public function __construct(Monolog $logger)
    {
        $this->logger = $logger;
    }

    /**
     * Call Monolog with the given method and parameters.
     *
     * @param string $method
     * @param array  $parameters
     *
     * @throws \Psr\Log\InvalidArgumentException
     *
     * @return mixed
     */
    public function __call(string $method, array $parameters)
    {
        return $this->logger->{$method}(...$parameters);
    }

    /**
     * Logs with an arbitrary level.
     *
     * @param mixed $level
     * @param mixed $message
     * @param array $context
     *
     * @throws \Psr\Log\InvalidArgumentException
     *
     * @return void
     */
    public function log($level, $message, array $context = []): void
    {
        $message = $this->formatMessage($message);

        if ($this->eventManager !== null) {
            // If the event dispatcher is set, we will pass along the parameters to the
            // log listeners. These are useful for building profilers or other tools
            // that aggregate all of the log messages for a given "request" cycle.
            $this->eventManager->trigger(
                new MessageLoggedEvent($this, $level, $message, $context)
            );
        }

        if (! \method_exists($this->logger, $level)) {
            throw new InvalidArgumentException(\sprintf(
                'Call to undefined method \Monolog\Logger::%s',
                $level
            ));
        }

        $this->logger->{$level}($message, $context);
    }

    /**
     * Get the underlying Monolog instance.
     *
     * @return \Monolog\Logger
     */
    public function getMonolog(): Monolog
    {
        return $this->logger;
    }

    /**
     * Format the parameters for the logger.
     *
     * @param mixed $message
     *
     * @return null|bool|float|int|object|string
     */
    private function formatMessage($message)
    {
        if (\is_array($message)) {
            return \var_export($message, true);
        }

        // @codeCoverageIgnoreStart
        if ($message instanceof Jsonable) {
            return $message->toJson();
        }

        if ($message instanceof Arrayable) {
            return \var_export($message->toArray(), true);
        }
        // @codeCoverageIgnoreEnd

        return $message;
    }
}


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

class Group
{
    /**
     * Merge route groups into a new array.
     *
     * @param array $new
     * @param array $old
     *
     * @return array
     */
    public static function merge(array $new, array $old): array
    {
        if (isset($new['domain'])) {
            unset($old['domain']);
        }

        $new = \array_merge(static::formatAs($new, $old), [
            'namespace' => static::formatNamespace($new, $old),
            'prefix'    => static::formatGroupPrefix($new, $old),
            'where'     => static::formatWhere($new, $old),
            'suffix'    => static::formatGroupSuffix($new, $old),
        ]);

        foreach (['namespace', 'prefix', 'suffix', 'where', 'as'] as $name) {
            if (isset($old[$name])) {
                unset($old[$name]);
            }
        }

        return \array_merge_recursive($old, $new);
    }

    /**
     * Format the uses prefix for the new group attributes.
     *
     * @param array $new
     * @param array $old
     *
     * @return null|string
     */
    protected static function formatNamespace(array $new, array $old): ?string
    {
        if (isset($new['namespace'])) {
            if (\strpos($new['namespace'], '\\') === 0) {
                return \trim($new['namespace'], '\\');
            }

            return isset($old['namespace']) ?
                \trim($old['namespace'], '\\') . '\\' . \trim($new['namespace'], '\\') :
                \trim($new['namespace'], '\\');
        }

        return $old['namespace'] ?? null;
    }

    /**
     * Format the prefix for the new group attributes.
     *
     * @param array $new
     * @param array $old
     *
     * @return null|string
     */
    protected static function formatGroupPrefix(array $new, array $old): ?string
    {
        $oldPrefix = $old['prefix'] ?? null;

        if (isset($new['prefix'])) {
            return \trim($oldPrefix, '/') . '/' . \trim($new['prefix'], '/');
        }

        return $oldPrefix;
    }

    /**
     * Format the suffix for the new group attributes.
     *
     * @param array $new
     * @param array $old
     *
     * @return null|string
     */
    protected static function formatGroupSuffix(array $new, array $old): ?string
    {
        $oldSuffix = $old['suffix'] ?? null;

        if (isset($new['suffix'])) {
            return \trim($new['suffix']) . \trim($oldSuffix);
        }

        return $oldSuffix;
    }

    /**
     * Format the "wheres" for the new group attributes.
     *
     * @param array $new
     * @param array $old
     *
     * @return array
     */
    protected static function formatWhere(array $new, array $old): array
    {
        return \array_merge(
            $old['where'] ?? [],
            $new['where'] ?? []
        );
    }

    /**
     * Format the "as" clause of the new group attributes.
     *
     * @param array $new
     * @param array $old
     *
     * @return array
     */
    protected static function formatAs(array $new, array $old): array
    {
        if (isset($old['as'])) {
            $new['as'] = $old['as'] . ($new['as'] ?? '');
        }

        return $new;
    }
}


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

use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Throwable;
use Viserio\Component\Contract\Exception\Displayer as DisplayerContract;
use Viserio\Component\Contract\HttpFactory\Traits\ResponseFactoryAwareTrait;
use Viserio\Component\Contract\View\Factory as FactoryContract;
use Viserio\Component\Exception\ExceptionInfo;

class ViewDisplayer implements DisplayerContract
{
    use ResponseFactoryAwareTrait;

    /**
     * The view factory instance.
     *
     * @var \Viserio\Component\Contract\View\Factory
     */
    protected $factory;

    /**
     * Create a new html displayer instance.
     *
     * @param \Psr\Http\Message\ResponseFactoryInterface $responseFactory
     * @param \Viserio\Component\Contract\View\Factory   $factory
     */
    public function __construct(ResponseFactoryInterface $responseFactory, FactoryContract $factory)
    {
        $this->responseFactory = $responseFactory;
        $this->factory         = $factory;
    }

    /**
     * {@inheritdoc}
     */
    public function display(Throwable $exception, string $id, int $code, array $headers): ResponseInterface
    {
        $response = $this->responseFactory->createResponse($code);

        foreach (\array_merge($headers, ['content-type' => $this->getContentType()]) as $header => $value) {
            $response = $response->withAddedHeader($header, $value);
        }

        $view = $this->factory->create("errors.{$code}", ExceptionInfo::generate($id, $code));
        $view->with('exception', $exception);

        $body = $response->getBody();
        $body->write((string) $view);
        $body->rewind();

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

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

    /**
     * {@inheritdoc}
     */
    public function canDisplay(Throwable $original, Throwable $transformed, int $code): bool
    {
        return $this->factory->exists("errors.{$code}");
    }

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


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

use Dotenv\Dotenv;
use Dotenv\Exception\InvalidFileException;
use Dotenv\Exception\InvalidPathException;
use Symfony\Component\Console\Input\ArgvInput;
use Viserio\Component\Contract\Foundation\Bootstrap as BootstrapContract;
use Viserio\Component\Contract\Foundation\Kernel as KernelContract;
use Viserio\Component\Support\Debug\Dumper;
use Viserio\Component\Support\Env;

class LoadEnvironmentVariables implements BootstrapContract
{
    /**
     * {@inheritdoc}
     */
    public static function getPriority(): int
    {
        return 32;
    }

    /**
     * {@inheritdoc}
     */
    public static function bootstrap(KernelContract $kernel): void
    {
        if (\file_exists($kernel->getStoragePath('config.cache.php'))) {
            return;
        }

        $env = Env::get('APP_ENV');

        if ($env === null && ! class_exists(Dotenv::class)) {
            $message = '[APP_ENV] environment variable is not defined. ';
            $message .= 'You need to define environment variables for configuration or run [composer require vlucas/phpdotenv] as a Composer dependency to load variables from a .env file.';

            throw new \RuntimeException($message);
        }

        if (! class_exists(Dotenv::class)) {
            $kernel->detectEnvironment(static function () use ($env) {
                return $env ?? 'prod';
            });

            return;
        }

        static::checkForSpecificEnvironmentFile($kernel, $env);

        try {
            (new Dotenv($kernel->getEnvironmentPath(), $kernel->getEnvironmentFile()))->load();

            $kernel->detectEnvironment(static function () use ($env) {
                return $env ?? Env::get('APP_ENV', 'prod');
            });
        } catch (InvalidPathException $exception) {
        } catch (InvalidFileException $exception) {
            Dumper::dump($exception->getMessage());

            die(1);
        }
    }

    /**
     * Detect if a custom environment file matching the APP_ENV exists.
     *
     * @param \Viserio\Component\Contract\Foundation\Kernel $kernel
     * @param null|string                                   $env
     *
     * @return void
     */
    protected static function checkForSpecificEnvironmentFile(KernelContract $kernel, ?string $env): void
    {
        if ($kernel->isRunningInConsole() && ($input = new ArgvInput())->hasParameterOption(['--env', '-e'])) {
            if (static::setEnvironmentFilePath(
                $kernel,
                $kernel->getEnvironmentFile() . '.' . $input->getParameterOption(['--env', '-e'])
            )) {
                return;
            }
        }

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

        static::setEnvironmentFilePath($kernel, $kernel->getEnvironmentFile() . '.' . $env);
    }

    /**
     * Load a custom environment file.
     *
     * @param \Viserio\Component\Contract\Foundation\Kernel $kernel
     * @param string                                        $file
     *
     * @return bool
     */
    protected static function setEnvironmentFilePath(KernelContract $kernel, string $file): bool
    {
        if (\file_exists($kernel->getEnvironmentPath() . \DIRECTORY_SEPARATOR . $file)) {
            $kernel->loadEnvironmentFrom($file);

            return true;
        }

        return false;
    }
}


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

use Aws\Ses\SesClient;
use Closure;
use GuzzleHttp\Client as HttpClient;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Swift_SendmailTransport;
use Swift_SmtpTransport;
use Swift_Transport;
use Viserio\Component\Contract\Mail\Exception\InvalidArgumentException;
use Viserio\Component\Mail\Transport\ArrayTransport;
use Viserio\Component\Mail\Transport\LogTransport;
use Viserio\Component\Mail\Transport\MailgunTransport;
use Viserio\Component\Mail\Transport\MandrillTransport;
use Viserio\Component\Mail\Transport\SesTransport;
use Viserio\Component\Mail\Transport\SparkPostTransport;
use Viserio\Component\Support\Str;

class TransportFactory implements LoggerAwareInterface
{
    use LoggerAwareTrait;

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

    /**
     * The registered custom transformer creators.
     *
     * @var array
     */
    protected $extensions = [];

    /**
     * Get all of the created "transports".
     *
     * @return array
     */
    public function getTransports(): array
    {
        return $this->transports;
    }

    /**
     * Get a transport instance.
     *
     * @param string $transport
     * @param array  $config
     *
     * @throws \Viserio\Component\Contract\Mail\Exception\InvalidArgumentException
     *
     * @return \Swift_Transport
     */
    public function getTransport(string $transport, array $config): Swift_Transport
    {
        // If the given transport has not been created before, we will create the instances
        // here and cache it so we can return it next time very quickly. If there is
        // already a transport created by this name, we'll just return that instance.
        if (! isset($this->transports[$transport])) {
            $this->transports[$transport] = $this->createTransport($transport, $config);
        }

        return $this->transports[$transport];
    }

    /**
     * Make a new transport instance.
     *
     * @param string $transport
     * @param array  $config
     *
     * @throws \Viserio\Component\Contract\Mail\Exception\InvalidArgumentException
     *
     * @return \Swift_Transport
     */
    public function createTransport(string $transport, array $config): Swift_Transport
    {
        $method = 'create' . Str::studly($transport) . 'Transport';

        $config['name'] = $transport;

        return $this->create($config, $method);
    }

    /**
     * Check if the given transport is supported.
     *
     * @param string $transport
     *
     * @return bool
     */
    public function hasTransport(string $transport): bool
    {
        $method = 'create' . Str::studly($transport) . 'Transport';

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

    /**
     * {@inheritdoc}
     */
    public function extend(string $driver, Closure $callback): void
    {
        $this->extensions[$driver] = $callback->bindTo($this, $this);
    }

    /**
     * Make a new driver instance.
     *
     * @param array  $config
     * @param string $method
     *
     * @throws \Viserio\Component\Contract\Mail\Exception\InvalidArgumentException
     *
     * @return \Swift_Transport
     */
    protected function create(array $config, string $method): Swift_Transport
    {
        if (isset($this->extensions[$config['name']])) {
            return $this->callCustomCreator($config['name'], $config);
        }

        if (\method_exists($this, $method)) {
            return $this->{$method}($config);
        }

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

    /**
     * Call a custom connection / driver creator.
     *
     * @param string $extension
     * @param array  $config
     *
     * @return mixed
     */
    protected function callCustomCreator(string $extension, array $config = [])
    {
        return $this->extensions[$extension]($config);
    }

    /**
     * Create an instance of the Log Swift Transport driver.
     *
     * @return \Viserio\Component\Mail\Transport\LogTransport
     */
    protected function createLogTransport(): LogTransport
    {
        return new LogTransport($this->logger);
    }

    /**
     * Create an instance of the Log Swift Transport driver.
     *
     * @return \Viserio\Component\Mail\Transport\ArrayTransport
     */
    protected function createArrayTransport(): ArrayTransport
    {
        return new ArrayTransport();
    }

    /**
     * Create an instance of the Sendmail Swift Transport driver.
     *
     * @param array $config
     *
     * @return \Swift_SendmailTransport
     */
    protected function createSendmailTransport(array $config): Swift_SendmailTransport
    {
        return new Swift_SendmailTransport($config['command'] ?? '/usr/sbin/sendmail -bs');
    }

    /**
     * Create an instance of the SMTP Swift Transport driver.
     *
     * @param array $config
     *
     * @return \Swift_SmtpTransport
     */
    protected function createSmtpTransport(array $config): Swift_SmtpTransport
    {
        // The Swift SMTP transport instance will allow us to use any SMTP backend
        // for delivering mail such as Amazon SES, Sendgrid or a custom server
        // a developer has available.
        $transport = new Swift_SmtpTransport(
            $config['host'],
            $config['port']
        );

        if (isset($config['encryption'])) {
            $transport->setEncryption($config['encryption']);
        }

        // Once we have the transport we will check for the presence of a username
        // and password.
        if (isset($config['username'], $config['password'])) {
            $transport->setUsername($config['username']);
            $transport->setPassword($config['password']);
        }

        if (isset($config['stream'])) {
            $transport->setStreamOptions($config['stream']);
        }

        return $transport;
    }

    /**
     * Create an instance of the Mailgun Swift Transport driver.
     *
     * @param array $config
     *
     * @return \Viserio\Component\Mail\Transport\MailgunTransport
     */
    protected function createMailgunTransport(array $config): MailgunTransport
    {
        return new MailgunTransport(
            $this->getHttpClient($config),
            $config['secret'],
            $config['domain'],
            $config['base_url'] ?? null
        );
    }

    /**
     * Create an instance of the Mandrill Swift Transport driver.
     *
     * @param array $config
     *
     * @return \Viserio\Component\Mail\Transport\MandrillTransport
     */
    protected function createMandrillTransport(array $config): MandrillTransport
    {
        return new MandrillTransport(
            $this->getHttpClient($config),
            $config['secret']
        );
    }

    /**
     * Create an instance of the SparkPost Swift Transport driver.
     *
     * @param array $config
     *
     * @return \Viserio\Component\Mail\Transport\SparkPostTransport
     */
    protected function createSparkPostTransport(array $config): SparkPostTransport
    {
        return new SparkPostTransport(
            $this->getHttpClient($config),
            $config['secret'],
            $config['options'] ?? [],
            $config['endpoint'] ?? null
        );
    }

    /**
     * Create an instance of the Amazon SES Swift Transport driver.
     *
     * @param array $config
     *
     * @throws \InvalidArgumentException
     *
     * @return \Viserio\Component\Mail\Transport\SesTransport
     */
    protected function createSesTransport(array $config): SesTransport
    {
        $config += [
            'version' => 'latest',
            'service' => 'email',
        ];

        if (isset($config['key'], $config['secret'])) {
            $config['credentials'] = \array_intersect_key($config, \array_flip(['key', 'secret', 'token']));
        }

        return new SesTransport(new SesClient($config));
    }

    /**
     * Get a fresh Guzzle HTTP client instance.
     *
     * @param array $config
     *
     * @return \GuzzleHttp\Client
     */
    protected function getHttpClient(array $config): HttpClient
    {
        $guzzleConfig = $config['guzzle'] ?? [];

        $guzzleConfig['connect_timeout'] = 90;

        return new HttpClient($guzzleConfig);
    }
}


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

use Interop\Container\ServiceProviderInterface;
use Psr\Container\ContainerInterface;
use Viserio\Component\Config\ParameterProcessor\EnvParameterProcessor;
use Viserio\Component\Config\Repository;
use Viserio\Component\Contract\Config\Repository as RepositoryContract;
use Viserio\Component\Contract\Parser\Loader as LoaderContract;

class ConfigServiceProvider implements ServiceProviderInterface
{
    /**
     * {@inheritdoc}
     */
    public function getFactories(): array
    {
        return [
            RepositoryContract::class => [self::class, 'createRepository'],
            Repository::class         => static function (ContainerInterface $container) {
                return $container->get(RepositoryContract::class);
            },
            'config' => static function (ContainerInterface $container) {
                return $container->get(RepositoryContract::class);
            },
        ];
    }

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

    /**
     * Create a new Repository instance.
     *
     * @param \Psr\Container\ContainerInterface $container
     *
     * @return \Viserio\Component\Contract\Config\Repository
     */
    public static function createRepository(ContainerInterface $container): RepositoryContract
    {
        $config = new Repository();

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

        $config->addParameterProcessor(new EnvParameterProcessor());

        return $config;
    }
}


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

use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;
use Viserio\Component\Support\Str as ViserioStr;

class StrExtension extends AbstractExtension
{
    /**
     * @var array|callable
     */
    protected $callback = ViserioStr::class;

    /**
     * Return the string object callback.
     *
     * @return array|callable
     */
    public function getCallback()
    {
        return $this->callback;
    }

    /**
     * Set a new string callback.
     *
     * @param array|callable $callback
     *
     * @return void
     */
    public function setCallback($callback): void
    {
        $this->callback = $callback;
    }

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

    /**
     * {@inheritdoc}
     */
    public function getFunctions(): array
    {
        return [
            new TwigFunction(
                'str_*',
                function (string $name) {
                    $arguments = \array_slice(\func_get_args(), 1);
                    $name = (string) ViserioStr::camelize($name);

                    return $this->callback::$name(...$arguments);
                }
            ),
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function getFilters(): array
    {
        return [
            new TwigFilter(
                'str_*',
                function (string $name) {
                    $arguments = \array_slice(\func_get_args(), 1);
                    $name = (string) ViserioStr::camelize($name);

                    return $this->callback::$name(...$arguments);
                }
            ),
        ];
    }
}


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

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Viserio\Component\Contract\Profiler\AssetAware as AssetAwareContract;
use Viserio\Component\Contract\Profiler\TooltipAware as TooltipAwareContract;

class AjaxRequestsDataCollector extends AbstractDataCollector implements
    AssetAwareContract,
    TooltipAwareContract
{
    /**
     * {@inheritdoc}
     */
    public function collect(ServerRequestInterface $serverRequest, ResponseInterface $response): void
    {
        // all collecting is done client side
    }

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

    /**
     * {@inheritdoc}
     */
    public function getTooltip(): string
    {
        return $this->createTooltipGroup([
            '0 AJAX requests' => '',
            '

                        Method
                        Type
                        Status
                        URL
                        Time
                        Profile

',
        ]);
    }

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

    /**
     * {@inheritdoc}
     */
    public function reset(): void
    {
        // all collecting is done client side
    }
}


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

use League\Flysystem\AdapterInterface;
use League\Flysystem\Sftp\SftpAdapter;
use Viserio\Component\Contract\Filesystem\Connector as ConnectorContract;
use Viserio\Component\Contract\OptionsResolver\Exception\InvalidArgumentException;
use Viserio\Component\Contract\OptionsResolver\RequiresConfig as RequiresConfigContract;
use Viserio\Component\Contract\OptionsResolver\RequiresMandatoryOptions as RequiresMandatoryOptionsContract;
use Viserio\Component\Contract\OptionsResolver\RequiresValidatedConfig as RequiresValidatedConfigContract;
use Viserio\Component\OptionsResolver\Traits\OptionsResolverTrait;

final class SftpConnector implements
    ConnectorContract,
    RequiresConfigContract,
    RequiresMandatoryOptionsContract,
    RequiresValidatedConfigContract
{
    use OptionsResolverTrait;

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

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

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

    /**
     * {@inheritdoc}
     */
    public static function getOptionValidators(): array
    {
        return [
            'host'       => ['string'],
            'port'       => ['string', 'integer'],
            'username'   => ['string'],
            'password'   => ['string'],
            'privateKey' => ['string'],
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function connect(): AdapterInterface
    {
        if (! isset($this->resolvedOptions['password']) && ! isset($this->resolvedOptions['privateKey'])) {
            throw new InvalidArgumentException('The sftp connector requires [password] or [privateKey] configuration.');
        }

        return new SftpAdapter($this->resolvedOptions);
    }
}


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

use Narrowspark\Arr\Arr;

class ParameterProcessor extends AbstractParameterProcessor
{
    /**
     * A array of parameters.
     *
     * @var array
     */
    private $parameters;

    /**
     * Create a new ParameterProcessor instance.
     *
     * @param array $parameters
     */
    public function __construct(array $parameters)
    {
        $this->parameters = $parameters;
    }

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

    /**
     * {@inheritdoc}
     */
    public function process(string $data)
    {
        $parameterKey = $this->parseParameter($data);

        return $this->replaceData($data, $parameterKey, Arr::get($this->parameters, $parameterKey));
    }
}


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

use Viserio\Component\Contract\Routing\MiddlewareAware as MiddlewareAwareContract;
use Viserio\Component\Contract\Routing\PendingResourceRegistration as PendingResourceRegistrationContract;
use Viserio\Component\Routing\Traits\MiddlewareValidatorTrait;

class PendingResourceRegistration implements PendingResourceRegistrationContract
{
    use MiddlewareValidatorTrait;

    /**
     * The resource registrar.
     *
     * @var \Viserio\Component\Routing\ResourceRegistrar
     */
    protected $registrar;

    /**
     * The resource name.
     *
     * @var string
     */
    protected $name;

    /**
     * The resource controller.
     *
     * @var string
     */
    protected $controller;

    /**
     * The resource options.
     *
     * @var array
     */
    protected $options = [];

    /**
     * Create a new pending resource registration instance.
     *
     * @param \Viserio\Component\Routing\ResourceRegistrar $registrar
     * @param string                                       $name
     * @param string                                       $controller
     * @param array                                        $options
     */
    public function __construct(ResourceRegistrar $registrar, string $name, string $controller, array $options)
    {
        $this->name       = $name;
        $this->options    = $options;
        $this->registrar  = $registrar;
        $this->controller = $controller;
    }

    /**
     * Handle the object's destruction.
     *
     * @return void
     */
    public function __destruct()
    {
        $this->registrar->register($this->name, $this->controller, $this->options);
    }

    /**
     * {@inheritdoc}
     */
    public function setName(string $method, string $name): PendingResourceRegistrationContract
    {
        $this->options['names'][$method] = $name;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function only(array $methods): PendingResourceRegistrationContract
    {
        $this->options['only'] = $methods;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function except(array $methods): PendingResourceRegistrationContract
    {
        $this->options['except'] = $methods;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function addNames(array $names): PendingResourceRegistrationContract
    {
        $this->options['names'] = $names;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function setParameters(array $parameters): PendingResourceRegistrationContract
    {
        $this->options['parameters'] = $parameters;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function setParameter(string $previous, string $new): PendingResourceRegistrationContract
    {
        $this->options['parameters'][$previous] = $new;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function withMiddleware($middleware): MiddlewareAwareContract
    {
        $this->validateGivenMiddleware($middleware);

        $this->options['middleware'] = $middleware;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function withoutMiddleware($middleware): MiddlewareAwareContract
    {
        $this->validateGivenMiddleware($middleware);

        $this->options['bypass'] = $middleware;

        return $this;
    }

    /**
     * First:  Validates the given input.
     * Second: Checks if given middleware or list of middleware have the right interface.
     *
     * @param array|object|string $middleware
     *
     * @throws \RuntimeException if wrong input is given
     * @throws \LogicException   if \Psr\Http\Server\MiddlewareInterface was not found
     *
     * @return void
     */
    private function validateGivenMiddleware($middleware): void
    {
        $this->validateInput($middleware);

        if (\is_array($middleware)) {
            foreach ($middleware as $mw) {
                $this->validateMiddleware($mw);
            }
        } else {
            $this->validateMiddleware($middleware);
        }
    }
}


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

use Viserio\Component\Contract\Routing\Exception\RuntimeException;
use Viserio\Component\Contract\Routing\SegmentMatcher as SegmentMatcherContract;

final class RouteTreeNode
{
    /**
     * All segment matcher.
     *
     * @var \Viserio\Component\Contract\Routing\SegmentMatcher[]
     */
    private $matchers;

    /**
     * Actual route content.
     *
     * @var \Viserio\Component\Routing\TreeGenerator\ChildrenNodeCollection|\Viserio\Component\Routing\TreeGenerator\MatchedRouteDataMap
     */
    private $contents;

    /**
     * Create a new child node collection instance.
     *
     * @param \Viserio\Component\Contract\Routing\SegmentMatcher[]                                                                         $matchers
     * @param \Viserio\Component\Routing\TreeGenerator\ChildrenNodeCollection|\Viserio\Component\Routing\TreeGenerator\MatchedRouteDataMap $contents
     *
     * @throws \Viserio\Component\Contract\Routing\Exception\RuntimeException
     */
    public function __construct(array $matchers, $contents)
    {
        if (\count($matchers) === 0) {
            throw new RuntimeException(\sprintf('Cannot construct [%s], matchers must not be empty.', __CLASS__));
        }

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

    /**
     * Get all matchers from array.
     *
     * @return \Viserio\Component\Contract\Routing\SegmentMatcher[]
     */
    public function getMatchers(): array
    {
        return $this->matchers;
    }

    /**
     * Get actual route content.
     *
     * @return \Viserio\Component\Routing\TreeGenerator\ChildrenNodeCollection|\Viserio\Component\Routing\TreeGenerator\MatchedRouteDataMap
     */
    public function getContents()
    {
        return $this->contents;
    }

    /**
     * Get the first matcher from array.
     *
     * @return \Viserio\Component\Contract\Routing\SegmentMatcher
     */
    public function getFirstMatcher(): SegmentMatcherContract
    {
        return $this->matchers[\min(\array_keys($this->matchers))];
    }

    /**
     * Check if content is a leaf node.
     *
     * @return bool
     */
    public function isLeafNode(): bool
    {
        return $this->contents instanceof MatchedRouteDataMap;
    }

    /**
     * Check if content is a parent node.
     *
     * @return bool
     */
    public function isParentNode(): bool
    {
        return $this->contents instanceof ChildrenNodeCollection;
    }

    /**
     * Update RouteTreeNode class.
     *
     * @param array                                                                                $matchers
     * @param \Viserio\Component\Routing\TreeGenerator\ChildrenNodeCollection|\Viserio\Component\Routing\TreeGenerator\MatchedRouteDataMap $contents
     *
     * @throws \RuntimeException
     *
     * @return \Viserio\Component\Routing\TreeGenerator\RouteTreeNode
     */
    public function update(array $matchers, $contents): RouteTreeNode
    {
        if ($this->matchers === $matchers && $this->contents === $contents) {
            return $this;
        }

        $clone           = clone $this;
        $clone->matchers = $matchers;
        $clone->contents = $contents;

        return $clone;
    }
}


<?php
declare(strict_types=1);
namespace Viserio\Bridge\Monolog\Handler;

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Monolog\Handler\AbstractProcessingHandler;
use Monolog\Logger;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Viserio\Bridge\Monolog\Formatter\ConsoleFormatter;
use Viserio\Component\Console\ConsoleEvents;
use Viserio\Component\Console\Event\ConsoleCommandEvent;
use Viserio\Component\Contract\Events\EventManager as EventManagerContract;
use Viserio\Component\Contract\Events\Traits\EventManagerAwareTrait;

/**
 * Writes logs to the console output depending on its verbosity setting.
 *
 * It is disabled by default and gets activated as soon as a command is executed.
 * Instead of listening to the console events, the output can also be set manually.
 *
 * The minimum logging level at which this handler will be triggered depends on the
 * verbosity setting of the console output. The default mapping is:
 * - OutputInterface::VERBOSITY_NORMAL will show all WARNING and higher logs
 * - OutputInterface::VERBOSITY_VERBOSE (-v) will show all NOTICE and higher logs
 * - OutputInterface::VERBOSITY_VERY_VERBOSE (-vv) will show all INFO and higher logs
 * - OutputInterface::VERBOSITY_DEBUG (-vvv) will show all DEBUG and higher logs, i.e. all logs
 *
 * This mapping can be customized with the $verbosityLevelMap constructor parameter.
 *
 * @author Tobias Schultze <http://tobion.de>
 */
class ConsoleHandler extends AbstractProcessingHandler
{
    use EventManagerAwareTrait;

    /**
     * A output instance.
     *
     * @var null|\Symfony\Component\Console\Output\OutputInterface
     */
    private $output;

    /**
     * A verbosity level to monolog level mapper.
     *
     * @var array
     */
    private $verbosityLevelMap = [
        OutputInterface::VERBOSITY_QUIET        => Logger::ERROR,
        OutputInterface::VERBOSITY_NORMAL       => Logger::WARNING,
        OutputInterface::VERBOSITY_VERBOSE      => Logger::NOTICE,
        OutputInterface::VERBOSITY_VERY_VERBOSE => Logger::INFO,
        OutputInterface::VERBOSITY_DEBUG        => Logger::DEBUG,
    ];

    /**
     * Constructor.
     *
     * @param null|OutputInterface $output            The console output to use (the handler remains disabled when passing null
     *                                                until the output is set, e.g. by using console events)
     * @param bool                 $bubble            Whether the messages that are handled can bubble up the stack
     * @param array                $verbosityLevelMap Array that maps the OutputInterface verbosity to a minimum logging
     *                                                level (leave empty to use the default mapping)
     */
    public function __construct(OutputInterface $output = null, $bubble = true, array $verbosityLevelMap = [])
    {
        parent::__construct(Logger::DEBUG, $bubble);

        $this->output = $output;

        if (\count($verbosityLevelMap) !== 0) {
            $this->verbosityLevelMap = $verbosityLevelMap;
        }
    }

    /**
     * Sets the console output to use for printing logs.
     *
     * @param OutputInterface $output The console output to use
     */
    public function setOutput(OutputInterface $output): void
    {
        $this->output = $output;
    }

    /**
     * {@inheritdoc}
     */
    public function isHandling(array $record): bool
    {
        return $this->updateLevel() && parent::isHandling($record);
    }

    /**
     * {@inheritdoc}
     */
    public function handle(array $record): bool
    {
        // we have to update the logging level each time because the verbosity of the
        // console output might have changed in the meantime (it is not immutable)
        return $this->updateLevel() && parent::handle($record);
    }

    /**
     * Disables the output.
     */
    public function close(): void
    {
        $this->output = null;

        parent::close();
    }

    /**
     * Register needed events to event manager.
     *
     * @param \Viserio\Component\Contract\Events\EventManager $eventManager
     *
     * @return void
     */
    public function registerEvents(EventManagerContract $eventManager): void
    {
        // Before a command is executed, the handler gets activated and the console output
        // is set in order to know where to write the logs.
        $eventManager->attach(
            ConsoleEvents::COMMAND,
            function (ConsoleCommandEvent $event): void {
                $output = $event->getOutput();

                if ($output instanceof ConsoleOutputInterface) {
                    $output = $output->getErrorOutput();
                }

                $this->setOutput($output);
            },
            255
        );

        // After a command has been executed, it disables the output.
        $eventManager->attach(
            ConsoleEvents::TERMINATE,
            function (): void {
                $this->close();
            },
            -255
        );
    }

    /**
     * {@inheritdoc}
     */
    protected function write(array $record): void
    {
        // at this point we've determined for sure that we want to output the record, so use the output's own verbosity
        $this->output->write((string) $record['formatted'], false, $this->output->getVerbosity());
    }

    /**
     * {@inheritdoc}
     */
    protected function getDefaultFormatter()
    {
        if (! $this->output) {
            return new ConsoleFormatter();
        }

        return new ConsoleFormatter([
            'colors'    => $this->output->isDecorated(),
            'multiline' => OutputInterface::VERBOSITY_DEBUG <= $this->output->getVerbosity(),
        ]);
    }

    /**
     * Updates the logging level based on the verbosity setting of the console output.
     *
     * @return bool Whether the handler is enabled and verbosity is not set to quiet
     */
    private function updateLevel(): bool
    {
        if ($this->output === null) {
            return false;
        }

        $verbosity = $this->output->getVerbosity();

        if (isset($this->verbosityLevelMap[$verbosity])) {
            $this->setLevel($this->verbosityLevelMap[$verbosity]);
        } else {
            $this->setLevel(Logger::DEBUG);
        }

        return true;
    }
}


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

use Interop\Container\ServiceProviderInterface;
use Psr\Container\ContainerInterface;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Twig\Environment as TwigEnvironment;
use Twig\Lexer;
use Viserio\Bridge\Twig\Extension\ConfigExtension;
use Viserio\Bridge\Twig\Extension\DumpExtension;
use Viserio\Bridge\Twig\Extension\SessionExtension;
use Viserio\Bridge\Twig\Extension\StrExtension;
use Viserio\Bridge\Twig\Extension\TranslatorExtension;
use Viserio\Component\Contract\Config\Repository as RepositoryContract;
use Viserio\Component\Contract\Session\Store as StoreContract;
use Viserio\Component\Contract\Translation\TranslationManager as TranslationManagerContract;
use Viserio\Component\Support\Str;

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

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

    /**
     * Extend the twig environment.
     *
     * @param \Psr\Container\ContainerInterface $container
     * @param null|\Twig\Environment            $twig
     *
     * @return null|\Twig\Environment
     */
    public static function extendTwigEnvironment(
        ContainerInterface $container,
        ?TwigEnvironment $twig = null
    ): ?TwigEnvironment {
        if ($twig !== null) {
            if ($container->has(Lexer::class)) {
                $twig->setLexer($container->get(Lexer::class));
            }

            if ($twig->isDebug() && \class_exists(VarCloner::class)) {
                $twig->addExtension(new DumpExtension());
            }

            self::registerViserioTwigExtension($twig, $container);
        }

        return $twig;
    }

    /**
     * Register viserio twig extension.
     *
     * @param \Twig\Environment                 $twig
     * @param \Psr\Container\ContainerInterface $container
     *
     * @return void
     */
    protected static function registerViserioTwigExtension(TwigEnvironment $twig, ContainerInterface $container): void
    {
        if ($container->has(TranslationManagerContract::class)) {
            $twig->addExtension(new TranslatorExtension($container->get(TranslationManagerContract::class)));
        }

        if (\class_exists(Str::class)) {
            $twig->addExtension(new StrExtension());
        }

        if ($container->has(StoreContract::class)) {
            $twig->addExtension(new SessionExtension($container->get(StoreContract::class)));
        }

        if ($container->has(RepositoryContract::class)) {
            $twig->addExtension(new ConfigExtension($container->get(RepositoryContract::class)));
        }
    }
}


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

use Viserio\Component\Contract\Routing\Route as RouteContract;

final class MatchedRouteDataMap
{
    /**
     * @var array
     */
    private $httpMethodRouteMap = [];

    /**
     * Create a new matched route data map instance.
     *
     * @param array $httpMethodRouteMap
     */
    public function __construct(array $httpMethodRouteMap = [])
    {
        $this->httpMethodRouteMap = $httpMethodRouteMap;
    }

    /**
     * @return array
     */
    public function getHttpMethodRouteDataMap(): array
    {
        return $this->httpMethodRouteMap;
    }

    /**
     * Get all allowed http methods.
     *
     * @return array
     */
    public function allowedHttpMethods(): array
    {
        $allowedHttpMethods = [];

        foreach ($this->httpMethodRouteMap as $item) {
            foreach ($item[0] as $method) {
                $allowedHttpMethods[] = $method;
            }
        }

        return \array_values($allowedHttpMethods);
    }

    /**
     * Adds the supplied route to the matched route data map.
     *
     * @param \Viserio\Component\Contract\Routing\Route $route
     * @param array                                     $parameterIndexNameMap
     */
    public function addRoute(RouteContract $route, array $parameterIndexNameMap): void
    {
        $this->httpMethodRouteMap[] = [$route->getMethods(), [$parameterIndexNameMap, $route->getIdentifier()]];
    }
}


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

use Psr\Http\Message\StreamInterface;
use Throwable;
use Viserio\Component\Contract\Http\Exception\InvalidArgumentException;
use Viserio\Component\Contract\Http\Exception\RuntimeException;
use Viserio\Component\Contract\Http\Exception\UnexpectedValueException;

class Stream implements StreamInterface
{
    /**
     * Bit mask to determine if the stream is a pipe.
     *
     * This is octal as per header stat.h
     *
     * @var int
     */
    public const FSTAT_MODE_S_IFIFO = 0010000;

    /**
     * Resource modes.
     *
     * @var string
     *
     * @see http://php.net/manual/function.fopen.php
     * @see http://php.net/manual/en/function.gzopen.php
     */
    public const READABLE_MODES = '/r|a\+|ab\+|w\+|wb\+|x\+|xb\+|c\+|cb\+/';

    public const WRITABLE_MODES = '/a|w|r\+|rw|x|c/';

    /**
     * The underlying stream resource.
     *
     * @var resource
     */
    protected $stream;

    /**
     * Stream metadata.
     *
     * @var array
     */
    protected $meta;

    /**
     * Is this stream readable?
     *
     * @var bool
     */
    protected $readable;

    /**
     * Is this stream writable?
     *
     * @var bool
     */
    protected $writable;

    /**
     * Is this stream seekable?
     *
     * @var bool
     */
    protected $seekable;

    /**
     * The size of the stream if known.
     *
     * @var null|int
     */
    protected $size;

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

    /**
     * Is this stream a pipe?
     *
     * @var null|bool
     */
    protected $isPipe;

    /**
     * Stream type of a open stream.
     *
     * @var string
     */
    protected $streamType;

    /**
     * This constructor accepts an associative array of options.
     *
     * - size: (int) If a read stream would otherwise have an indeterminate
     *   size, but the size is known due to foreknowledge, then you can
     *   provide that size, in bytes.
     * - metadata: (array) Any additional metadata to return when the metadata
     *   of the stream is accessed.
     *
     * @param resource|string $stream  stream resource to wrap
     * @param array           $options associative array of options
     *                                 array[]
     *                                 ['mode']      string A optional option; Default mode is 'rb' for the string stream
     *                                 ['size']      int    A optional option; Size of the stream
     *                                 ['metadata']  array  A optional option; Metadata of the stream
     *
     * @throws \Viserio\Component\Contract\Http\Exception\UnexpectedValueException if the stream is not a stream resource
     */
    public function __construct($stream, array $options = [])
    {
        $error = null;

        if (\is_string($stream)) {
            $stream = Util::tryFopen($stream, $options['mode'] ?? 'rb');
        }

        if ($error !== null) {
            throw new InvalidArgumentException('Invalid stream reference provided.');
        }

        if (! \is_resource($stream) || \get_resource_type($stream) !== 'stream') {
            throw new UnexpectedValueException(
                'Invalid stream provided; must be a string stream identifier or stream resource.'
            );
        }

        $this->stream = $stream;

        if (isset($options['size'])) {
            $this->size = (int) $options['size'];
        }

        $this->meta = $options['metadata'] ?? [];

        $meta = \stream_get_meta_data($this->stream);

        $this->seekable   = ! $this->isPipe() && $meta['seekable'];
        $this->readable   = \preg_match(self::READABLE_MODES, $meta['mode']) === 1 || $this->isPipe();
        $this->writable   = \preg_match(self::WRITABLE_MODES, $meta['mode']) === 1;
        $this->uri        = $this->getMetadata('uri');
        $this->streamType = $meta['stream_type'] ?? 'unknown';
    }

    /**
     * Closes the stream when the destructed.
     *
     * @return void
     */
    public function __destruct()
    {
        $this->close();
    }

    /**
     * {@inheritdoc}
     */
    public function __toString(): string
    {
        try {
            if ($this->isSeekable()) {
                $this->rewind();
            }

            return $this->getContents();
        } catch (Throwable $exception) {
            // Really, PHP? https://bugs.php.net/bug.php?id=53648
            \trigger_error(self::class . '::__toString exception: ' . (string) $exception, \E_USER_ERROR);

            return '';
        }
    }

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

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

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

    /**
     * {@inheritdoc}
     */
    public function getSize(): ?int
    {
        if ($this->size !== null) {
            return $this->size;
        }

        if (! isset($this->stream)) {
            return null;
        }

        // Clear the stat cache if the stream has a URI
        if ($this->uri) {
            \clearstatcache(true, $this->uri);
        }

        $stats = \fstat($this->stream);

        if (isset($stats['size']) && ! $this->isPipe()) {
            $this->size = $stats['size'];
        }

        return $this->size;
    }

    /**
     * {@inheritdoc}
     */
    public function getContents(): string
    {
        if (! isset($this->stream)) {
            throw new RuntimeException('Stream is detached.');
        }

        $contents = \stream_get_contents($this->stream);

        if ($contents === false) {
            throw new RuntimeException('Unable to read stream contents.');
        }

        return $contents;
    }

    /**
     * {@inheritdoc}
     */
    public function close(): void
    {
        if (isset($this->stream)) {
            if (\is_resource($this->stream)) {
                if ($this->isPipe()) {
                    \pclose($this->stream);
                } elseif ($this->streamType === 'ZLIB') {
                    \gzclose($this->stream);
                } else {
                    \fclose($this->stream);
                }
            }

            $this->detach();
        }
    }

    /**
     * {@inheritdoc}
     */
    public function detach()
    {
        if (! isset($this->stream)) {
            return null;
        }

        $result = $this->stream;

        unset($this->stream);

        $this->uri      = '';
        $this->meta     = [];
        $this->size     = $this->isPipe   = null;
        $this->readable = $this->writable = $this->seekable = false;

        return $result;
    }

    /**
     * {@inheritdoc}
     *
     * @throws \RuntimeException If Stream is detached
     */
    public function eof(): bool
    {
        if (! isset($this->stream)) {
            throw new RuntimeException('Stream is detached.');
        }

        return \feof($this->stream);
    }

    /**
     * {@inheritdoc}
     *
     * @throws \RuntimeException If Stream is detached
     * @throws \RuntimeException If Unable to determine stream position
     */
    public function tell(): int
    {
        if (! isset($this->stream)) {
            throw new RuntimeException('Stream is detached.');
        }

        $result = \ftell($this->stream);

        if (! \is_int($result) || $this->isPipe()) {
            throw new RuntimeException('Unable to determine stream position.');
        }

        return $result;
    }

    /**
     * {@inheritdoc}
     */
    public function rewind(): void
    {
        $this->seek(0);
    }

    /**
     * {@inheritdoc}
     */
    public function seek($offset, $whence = \SEEK_SET): void
    {
        if (! isset($this->stream)) {
            throw new RuntimeException('Stream is detached.');
        }

        if (! $this->seekable) {
            throw new RuntimeException('Stream is not seekable.');
        }

        if (\fseek($this->stream, $offset, $whence) === -1) {
            throw new RuntimeException(
                'Unable to seek to stream position '
                . $offset . ' with whence ' . \var_export($whence, true) . '.'
            );
        }
    }

    /**
     * {@inheritdoc}
     */
    public function read($length): string
    {
        if (! isset($this->stream)) {
            throw new RuntimeException('Stream is detached.');
        }

        if (! $this->readable) {
            throw new RuntimeException('Cannot read from non-readable stream.');
        }

        if ($length < 0) {
            throw new RuntimeException('Length parameter cannot be negative.');
        }

        if ($length === 0) {
            return '';
        }

        $string = fread($this->stream, $length);

        if ($string === false) {
            throw new RuntimeException('Unable to read from stream.');
        }

        return $string;
    }

    /**
     * {@inheritdoc}
     */
    public function write($string): int
    {
        if (! isset($this->stream)) {
            throw new RuntimeException('Stream is detached.');
        }

        if (! $this->writable) {
            throw new RuntimeException('Cannot write to a non-writable stream.');
        }

        // We can't know the size after writing anything
        $this->size = null;
        $result     = \fwrite($this->stream, $string);

        if ($result === false) {
            throw new RuntimeException('Unable to write to stream.');
        }

        return $result;
    }

    /**
     * {@inheritdoc}
     */
    public function getMetadata($key = null)
    {
        if (! isset($this->stream)) {
            return $key ? null : [];
        }

        if (! $key) {
            return $this->meta + \stream_get_meta_data($this->stream);
        }

        if (isset($this->meta[$key])) {
            return $this->meta[$key];
        }

        $meta = \stream_get_meta_data($this->stream);

        return $meta[$key] ?? null;
    }

    /**
     * Returns whether or not the stream is a pipe.
     *
     * @return bool
     */
    private function isPipe(): bool
    {
        if ($this->isPipe === null) {
            $this->isPipe = false;

            if (isset($this->stream)) {
                $mode         = \fstat($this->stream)['mode'];
                $this->isPipe = ($mode & self::FSTAT_MODE_S_IFIFO) !== 0;
            }
        }

        return $this->isPipe;
    }
}


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

use finfo;
use RuntimeException;
use Viserio\Component\Contract\Parser\Exception\NotSupportedException;
use Viserio\Component\Contract\Parser\Parser as ParserContract;
use Viserio\Component\Parser\Parser\IniParser;
use Viserio\Component\Parser\Parser\JsonParser;
use Viserio\Component\Parser\Parser\PhpArrayParser;
use Viserio\Component\Parser\Parser\PoParser;
use Viserio\Component\Parser\Parser\QtParser;
use Viserio\Component\Parser\Parser\QueryStrParser;
use Viserio\Component\Parser\Parser\SerializeParser;
use Viserio\Component\Parser\Parser\TomlParser;
use Viserio\Component\Parser\Parser\XliffParser;
use Viserio\Component\Parser\Parser\XmlParser;
use Viserio\Component\Parser\Parser\YamlParser;

class Parser
{
    /**
     * Supported mime type formats.
     *
     * @var array
     */
    private static $supportedMimeTypes = [
        // XML
        'application/xml' => 'xml',
        'text/xml'        => 'xml',
        // Xliff
        'application/x-xliff+xml' => 'xlf',
        // JSON
        'application/json'         => 'json',
        'application/x-javascript' => 'json',
        'text/javascript'          => 'json',
        'text/x-javascript'        => 'json',
        'text/x-json'              => 'json',
        // YAML
        'text/yaml'          => 'yaml',
        'text/x-yaml'        => 'yaml',
        'application/yaml'   => 'yaml',
        'application/x-yaml' => 'yaml',
        // MISC
        'application/vnd.php.serialized'    => 'serialize',
        'application/x-www-form-urlencoded' => 'querystr',
    ];

    /**
     * All supported parser.
     *
     * @var array
     */
    private static $supportedParsers = [
        'ini'       => IniParser::class,
        'json'      => JsonParser::class,
        'php'       => PhpArrayParser::class,
        'po'        => PoParser::class,
        'querystr'  => QueryStrParser::class,
        'serialize' => SerializeParser::class,
        'toml'      => TomlParser::class,
        'ts'        => QtParser::class,
        'xml'       => XmlParser::class,
        'xlf'       => XliffParser::class,
        'yaml'      => YamlParser::class,
    ];

    /**
     * Add a new mime type with extension.
     *
     * @param string $mimeType
     * @param string $extension
     *
     * @return void
     */
    public function addMimeType(string $mimeType, string $extension): void
    {
        self::$supportedMimeTypes[$mimeType] = $extension;
    }

    /**
     * Add a new parser.
     *
     * @param \Viserio\Component\Contract\Parser\Parser $parser
     * @param string                                    $extension
     *
     * @return void
     */
    public function addParser(ParserContract $parser, string $extension): void
    {
        self::$supportedParsers[$extension] = $parser;
    }

    /**
     * Parse given file path or content string.
     *
     * @param string $payload
     *
     * @throws \Viserio\Component\Contract\Parser\Exception\ParseException
     * @throws \RuntimeException                                           if an error occurred during reading
     *
     * @return array
     */
    public function parse(string $payload): array
    {
        if ($payload === '') {
            return [];
        }

        $format = $this->getFormat($payload);

        if ($format !== 'php' && \is_file($payload)) {
            $payload = \file_get_contents($payload);

            if ($payload === false) {
                throw new RuntimeException(\sprintf('A error occurred during reading [%s]', $payload));
            }
        }

        return $this->getParser($format)->parse($payload);
    }

    /**
     * Get supported parser on extension or mime type.
     *
     * @param string $type
     *
     * @throws \Viserio\Component\Contract\Parser\Exception\NotSupportedException
     *
     * @return \Viserio\Component\Contract\Parser\Parser
     */
    public function getParser(string $type): ParserContract
    {
        if (isset(self::$supportedParsers[$type])) {
            return new self::$supportedParsers[$type]();
        }

        if (isset(self::$supportedMimeTypes[$type])) {
            $class = self::$supportedParsers[self::$supportedMimeTypes[$type]];

            if (\is_object($class) && $class instanceof ParserContract) {
                return $class;
            }

            return new $class();
        }

        throw new NotSupportedException(\sprintf('Given extension or mime type [%s] is not supported.', $type));
    }

    /**
     * Auto detect the payload data type using finfo and pathinfo.
     *
     * @param string $payload
     *
     * @return string Tries to return the short format code (xml, json, ...) else the mime type
     */
    protected function getFormat(string $payload): string
    {
        $format = '';

        if (\is_file($file = $payload)) {
            $format = \pathinfo($file, \PATHINFO_EXTENSION);
        } elseif (\is_string($payload)) {
            // try if content is json
            \json_decode($payload);

            if (\json_last_error() === \JSON_ERROR_NONE) {
                return 'json';
            }

            $format = (new finfo(\FILEINFO_MIME_TYPE))->buffer($payload);
        }

        return self::$supportedMimeTypes[$format] ?? $format;
    }
}


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

use Symfony\Component\Filesystem\Filesystem;
use Viserio\Component\Console\Command\AbstractCommand;
use Viserio\Component\Contract\OptionsResolver\RequiresComponentConfig as RequiresComponentConfigContract;
use Viserio\Component\Contract\OptionsResolver\RequiresMandatoryOptions as RequiresMandatoryOptionsContract;
use Viserio\Component\OptionsResolver\Traits\OptionsResolverTrait;

class CleanCommand extends AbstractCommand implements RequiresComponentConfigContract, RequiresMandatoryOptionsContract
{
    use OptionsResolverTrait;

    /**
     * The default command name.
     *
     * @var string
     */
    protected static $defaultName = 'twig:clear';

    /**
     * {@inheritdoc}
     */
    protected $description = 'Clean the Twig Cache';

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

    /**
     * Create a new CleanCommand instance.
     *
     * @param array|\ArrayAccess $config
     */
    public function __construct($config)
    {
        parent::__construct();

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

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

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

    /**
     * {@inheritdoc}
     */
    public function handle(): int
    {
        $cacheDir = $this->resolvedOptions['engines']['twig']['options']['cache'];

        (new Filesystem())->remove($cacheDir);

        @\rmdir($cacheDir);

        if (\is_dir($cacheDir)) {
            $this->error('Twig cache failed to be cleaned.');

            return 1;
        }

        $this->info('Twig cache cleaned.');

        return 0;
    }
}


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

use Interop\Container\ServiceProviderInterface;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ServerRequestFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UploadedFileFactoryInterface;
use Psr\Http\Message\UriFactoryInterface;
use Viserio\Component\HttpFactory\RequestFactory;
use Viserio\Component\HttpFactory\ResponseFactory;
use Viserio\Component\HttpFactory\ServerRequestFactory;
use Viserio\Component\HttpFactory\StreamFactory;
use Viserio\Component\HttpFactory\UploadedFileFactory;
use Viserio\Component\HttpFactory\UriFactory;

class HttpFactoryServiceProvider implements ServiceProviderInterface
{
    /**
     * {@inheritdoc}
     */
    public function getFactories(): array
    {
        return [
            RequestFactoryInterface::class => [self::class, 'createRequestFactory'],
            RequestFactory::class          => static function (ContainerInterface $container) {
                return $container->get(RequestFactoryInterface::class);
            },
            ResponseFactoryInterface::class => [self::class, 'createResponseFactory'],
            ResponseFactory::class          => static function (ContainerInterface $container) {
                return $container->get(ResponseFactoryInterface::class);
            },
            ServerRequestFactoryInterface::class => [self::class, 'createServerRequestFactory'],
            ServerRequestFactory::class          => static function (ContainerInterface $container) {
                return $container->get(ServerRequestFactoryInterface::class);
            },
            StreamFactoryInterface::class => [self::class, 'createStreamFactory'],
            StreamFactory::class          => static function (ContainerInterface $container) {
                return $container->get(StreamFactoryInterface::class);
            },
            UploadedFileFactoryInterface::class => [self::class, 'createUploadedFileFactory'],
            UploadedFileFactory::class          => static function (ContainerInterface $container) {
                return $container->get(UploadedFileFactoryInterface::class);
            },
            UriFactoryInterface::class => [self::class, 'createUriFactory'],
            UriFactory::class          => static function (ContainerInterface $container) {
                return $container->get(UriFactoryInterface::class);
            },
        ];
    }

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

    public static function createRequestFactory(): RequestFactory
    {
        return new RequestFactory();
    }

    public static function createResponseFactory(): ResponseFactory
    {
        return new ResponseFactory();
    }

    public static function createServerRequestFactory(): ServerRequestFactory
    {
        return new ServerRequestFactory();
    }

    public static function createStreamFactory(): StreamFactory
    {
        return new StreamFactory();
    }

    public static function createUploadedFileFactory(): UploadedFileFactory
    {
        return new UploadedFileFactory();
    }

    public static function createUriFactory(): UriFactory
    {
        return new UriFactory();
    }
}


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

use RuntimeException;
use Viserio\Component\Http\Stream;
use Viserio\Component\Http\Util;

class PhpInputStream extends AbstractStreamDecorator
{
    /**
     * Stream instance.
     *
     * @var \Psr\Http\Message\StreamInterface
     */
    protected $stream;

    /**
     * Cached content-.
     *
     * @var string
     */
    private $cache = '';

    /**
     * True or false if eof is reached.
     *
     * @var bool
     */
    private $reachedEof = false;

    /**
     * Create a new php input stream instance.
     *
     * @param resource|string $stream
     */
    public function __construct($stream = 'php://input')
    {
        if (\is_string($stream)) {
            $stream = Util::tryFopen($stream, 'r');
        }

        parent::__construct(new Stream($stream));
    }

    /**
     * {@inheritdoc}
     */
    public function __toString(): string
    {
        if ($this->reachedEof) {
            return $this->cache;
        }

        try {
            $this->getContents();
        } catch (RuntimeException $exception) {
            // Really, PHP? https://bugs.php.net/bug.php?id=53648
            \trigger_error(self::class . '::__toString exception: ' . (string) $exception, \E_USER_ERROR);

            return '';
        }

        return $this->cache;
    }

    /**
     * {@inheritdoc}
     */
    public function isWritable(): bool
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function read($length): string
    {
        $content = parent::read($length);

        if (! $this->reachedEof) {
            $this->cache .= $content;
        }

        if ($this->eof()) {
            $this->reachedEof = true;
        }

        return $content;
    }

    /**
     * {@inheritdoc}
     */
    public function getContents(): string
    {
        if ($this->reachedEof) {
            return $this->cache;
        }

        $contents = $this->stream->getContents();
        $this->cache .= $contents;

        if ($this->eof()) {
            $this->reachedEof = true;
        }

        return $contents;
    }
}


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

use Viserio\Component\Console\Command\AbstractCommand;
use Viserio\Component\Contract\Console\Kernel as ConsoleKernelContract;

class UpCommand extends AbstractCommand
{
    /**
     * {@inheritdoc}
     */
    protected static $defaultName = 'app:up';

    /**
     * {@inheritdoc}
     */
    protected $description = 'Bring the application out of maintenance mode';

    /**
     * {@inheritdoc}
     */
    public function handle(): int
    {
        $kernel = $this->getContainer()->get(ConsoleKernelContract::class);

        @\unlink($kernel->getStoragePath('framework' . \DIRECTORY_SEPARATOR . 'down'));

        $this->info('Application is now live.');

        return 0;
    }
}


<?php
declare(strict_types=1);
namespace Narrowspark\Benchmark\OptionsResolver;

/**
 * @BeforeMethods({"classSetUp"})
 * @Revs(10000)
 * @Iterations(10)
 * @Warmup(2)
 */
abstract class AbstractCase
{
    /**
     * @var array
     */
    protected $config;

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

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

    /**
     * Setup config and class.
     */
    public function classSetUp(): void
    {
        $this->config   = $this->getTestConfig();
        $this->configId = $this->isId ? 'orm_default' : null;
    }

    /**
     * Returns test config.
     *
     * @return array
     */
    protected function getTestConfig(): array
    {
        return require \dirname(__DIR__, 3) . \DIRECTORY_SEPARATOR . 'src' . \DIRECTORY_SEPARATOR . 'Viserio' . \DIRECTORY_SEPARATOR . 'Component' . \DIRECTORY_SEPARATOR . 'OptionsResolver' . \DIRECTORY_SEPARATOR . 'Tests' . \DIRECTORY_SEPARATOR . 'Fixture' . \DIRECTORY_SEPARATOR . 'testing.config.php';
    }
}