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

use Symfony\Component\Console\Descriptor\ApplicationDescription;
use Symfony\Component\Console\Descriptor\DescriptorInterface;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Helper\TableStyle;
use Symfony\Component\Console\Output\OutputInterface;
use Viserio\Component\Console\Application;
use Viserio\Component\Console\Command\AbstractCommand;

class TextDescriptor implements DescriptorInterface
{
    /**
     * Describes an object if supported.
     *
     * @param \Symfony\Component\Console\Output\OutputInterface  $output
     * @param \Viserio\Component\Console\Command\AbstractCommand $object
     * @param array                                              $options
     *
     * @return void
     */
    public function describe(OutputInterface $output, $object, array $options = []): void
    {
        /** @var Application $application */
        $application = $object->getApplication();

        $this->describeTitle($application, $output);

        $describedNamespace = $options['namespace'] ?? null;

        if ($describedNamespace !== null) {
            $output->write(\sprintf(
                "<comment>Available commands for the [%s] namespace</comment>\n\n",
                $describedNamespace
            ));
        }

        $this->describeUsage($output);
        $this->describeCommands($application, $object, $options);
    }

    /**
     * Describes the application title.
     *
     * @param \Viserio\Component\Console\Application            $application
     * @param \Symfony\Component\Console\Output\OutputInterface $output
     */
    private function describeTitle(Application $application, OutputInterface $output): void
    {
        $name    = $application->getName();
        $version = $application->getVersion();

        if ($name === 'UNKNOWN' && $version === 'UNKNOWN') {
            return;
        }

        $output->write(
            \sprintf(
                "\n<fg=white;options=bold>%s </> <fg=green;options=bold>%s</>\n\n",
                $name,
                $version
            )
        );
    }

    /**
     * Describes the application title.
     *
     * @param \Symfony\Component\Console\Output\OutputInterface $output
     *
     * @return void
     */
    private function describeUsage(OutputInterface $output): void
    {
        $binary = Application::cerebroBinary();

        $output->write("<fg=yellow;options=bold>USAGE:</> ${binary} <command> [options] [arguments]\n\n");
        $output->write("where <command> is one of:\n");
    }

    /**
     * Describes the application commands.
     *
     * @param \Viserio\Component\Console\Application             $application
     * @param \Viserio\Component\Console\Command\AbstractCommand $command
     * @param array                                              $options
     *
     * @return void
     */
    private function describeCommands(Application $application, AbstractCommand $command, array $options): void
    {
        $description = new ApplicationDescription(
            $application,
            $options['namespace'] ?? null,
            $options['show-hidden'] ?? false
        );

        Table::setStyleDefinition('zero', self::getZeroBorderStyle());

        $rows                        = [];
        $namespaceSortedCommandInfos = $this->getNamespaceSortedCommandInfos(
            $description->getCommands()
        );

        foreach ($namespaceSortedCommandInfos as $namespace => $infos) {
            $stringCommands     = '';
            $stringDescriptions = '';

            foreach ($infos as $info) {
                $description = '';

                if ($options['show-description'] ?? false) {
                    $description = $info['description'];
                }

                $stringCommands     .= '<fg=green>' . $info['command'] . "</>\n";
                $stringDescriptions .= $description . "\n";
            }

            $rows[] = [$namespace, $stringCommands, $stringDescriptions];
        }

        $command->table([], $rows, 'zero');
    }

    /**
     * Sort all application commands on namespace.
     *
     * @param array $commands
     *
     * @return array
     */
    private function getNamespaceSortedCommandInfos(array $commands): array
    {
        $namespaceSortedInfos = [];
        $regex                = '/^(.*)\:/';
        $binary               = Application::cerebroBinary();

        /** @var AbstractCommand $command */
        foreach ($commands as $name => $command) {
            \preg_match($regex, $name, $matches, \PREG_OFFSET_CAPTURE);

            $commandInfo = [
                'command'     => $binary . ' ' . $command->getSynopsis(),
                'description' => $command->getDescription(),
            ];

            if (\count($matches) === 0) {
                $namespaceSortedInfos[$name][] = $commandInfo;
            } else {
                $namespaceSortedInfos[$matches[1][0]][] = $commandInfo;
            }
        }

        return $namespaceSortedInfos;
    }

    /**
     * @return TableStyle
     */
    private static function getZeroBorderStyle(): TableStyle
    {
        $style = new TableStyle();
        $style->setHorizontalBorderChars(' ');
        $style->setVerticalBorderChars(' ');
        $style->setDefaultCrossingChar(' ');

        return $style;
    }
}


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

use Viserio\Component\Console\Command\AbstractCommand;

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

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

    /**
     * {@inheritdoc}
     */
    protected $description = 'Remove the configuration cache file';

    /**
     * {@inheritdoc}
     */
    public function handle(): int
    {
        $returnValue = @\unlink($this->getCachedConfigDirPath());

        $this->info('Configuration cache cleared!');

        return (int) $returnValue;
    }

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


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

use Closure;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use ReflectionFunction;
use ReflectionMethod;
use Viserio\Component\Contract\Cookie\Cookie as CookieContract;
use Viserio\Component\Contract\Profiler\AssetAware as AssetAwareContract;
use Viserio\Component\Contract\Profiler\PanelAware as PanelAwareContract;
use Viserio\Component\Contract\Profiler\TooltipAware as TooltipAwareContract;
use Viserio\Component\Contract\Routing\Route as RouteContract;
use Viserio\Component\Contract\Routing\Router as RouterContract;
use Viserio\Component\Contract\Session\Store as StoreContract;
use Viserio\Component\Cookie\Cookie;
use Viserio\Component\Cookie\RequestCookies;
use Viserio\Component\Cookie\ResponseCookies;
use Viserio\Component\Profiler\DataCollector\AbstractDataCollector;

class ViserioHttpDataCollector extends AbstractDataCollector implements
    TooltipAwareContract,
    AssetAwareContract,
    PanelAwareContract
{
    /**
     * A server request instance.
     *
     * @var \Psr\Http\Message\ServerRequestInterface
     */
    protected $serverRequest;

    /**
     * A response instance.
     *
     * @var \Psr\Http\Message\ResponseInterface
     */
    protected $response;

    /**
     * List of all request sessions.
     *
     * @var array
     */
    protected $sessions;

    /**
     * Current route.
     *
     * @var \Viserio\Component\Contract\Routing\Route
     */
    protected $route;

    /**
     * Path to the route dir.
     *
     * @var string
     */
    protected $routeDirPath = '';

    /**
     * Create a new viserio request and response data collector instance.
     *
     * @param \Viserio\Component\Contract\Routing\Router $router
     * @param string                                     $routeDirPath
     */
    public function __construct(RouterContract $router, string $routeDirPath)
    {
        $this->route        = $router->getCurrentRoute();
        $this->routeDirPath = $routeDirPath;
    }

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

        foreach ($this->serverRequest->getAttributes() as $name => $value) {
            if ($value instanceof StoreContract) {
                $sessions[] = $value;
            }
        }

        $this->sessions = $sessions;
    }

    /**
     * {@inheritdoc}
     */
    public function getMenu(): array
    {
        $statusCode = $this->response->getStatusCode();
        $status     = '';

        // Successful 2xx
        if ($statusCode >= 200 && $statusCode <= 226) {
            $status = 'response-status-green';
        // Redirection 3xx
        } elseif ($statusCode >= 300 && $statusCode <= 308) {
            $status = 'response-status-yellow';
        // Client Error 4xx
        } elseif ($statusCode >= 400 && $statusCode <= 511) {
            $status = 'response-status-red';
        }

        $tabInfos = [
            'label' => $statusCode,
            'class' => $status,
            'value' => '',
        ];

        if ($this->route !== null && $this->route->getName() !== null) {
            return \array_merge(
                $tabInfos,
                [
                    'label' => '@',
                    'value' => $this->route->getName(),
                ]
            );
        }

        if ($this->route !== null) {
            return \array_merge(
                $tabInfos,
                [
                    'value' => \implode(' | ', $this->route->getMethods()),
                ]
            );
        }

        return $tabInfos;
    }

    /**
     * {@inheritdoc}
     */
    public function getTooltip(): string
    {
        $routeInfos = $this->getRouteInformation($this->route);

        return $this->createTooltipGroup([
            'Methods'             => $routeInfos['methods'],
            'Uri'                 => $routeInfos['uri'],
            'With Middleware'     => $routeInfos['middleware'],
            'Without Middleware'  => $routeInfos['without_middleware'] ?? '',
            'Namespace'           => $routeInfos['namespace'],
            'Prefix'              => $routeInfos['prefix'] ?? 'null',
            'File'                => $routeInfos['file'] ?? '',
        ]);
    }

    /**
     * {@inheritdoc}
     */
    public function getPanel(): string
    {
        $request     = $this->serverRequest;
        $response    = $this->response;
        $session     = $request->getAttribute('session');
        $sessionMeta = [];

        if ($session !== null) {
            $sessionMeta = [
                'Created'           => \date(\DATE_RFC2822, $session->getFirstTrace()),
                'Last used'         => \date(\DATE_RFC2822, $session->getLastTrace()),
                'Last regeneration' => \date(\DATE_RFC2822, $session->getRegenerationTrace()),
                'requestsCount'     => $session->getRequestsCount(),
                'fingerprint'       => $session->getFingerprint(),
            ];
        }

        return $this->createTabs([
            [
                'name'    => 'Request',
                'content' => $this->createTable(
                    $request->getQueryParams(),
                    [
                        'name'       => 'Get Parameters',
                        'empty_text' => 'No GET parameters',
                    ]
                ) . $this->createTable(
                        $this->getParsedBody($request),
                    [
                        'name'       => 'Post Parameters',
                        'empty_text' => 'No POST parameters',
                    ]
                ) . $this->createTable(
                    $this->prepareRequestAttributes($request->getAttributes()),
                    ['name' => 'Request Attributes']
                ) . $this->createTable(
                    $this->prepareRequestHeaders($request->getHeaders()),
                    ['name' => 'Request Headers']
                ) . $this->createTable(
                    $this->prepareServerParams($request->getServerParams()),
                    ['name' => 'Server Parameters']
                ),
            ],
            [
                'name'    => 'Response',
                'content' => $this->createTable(
                    $response->getHeaders(),
                    ['name' => 'Response Headers']
                ),
            ],
            $this->createCookieTab($request, $response),
            [
                'name'    => 'Session',
                'content' => $this->createTable(
                    $sessionMeta,
                    [
                        'name'       => 'Session Metadata',
                        'empty_text' => 'No session metadata',
                        'vardumper'  => false,
                    ]
                ) . $this->createTable(
                    $session !== null ? $session->getAll() : [],
                    [
                        'name'       => 'Session Attributes',
                        'empty_text' => 'No session attributes',
                    ]
                ),
            ],
            [
                'name'    => 'Flashes',
                'content' => $this->createTable(
                    $session !== null && $session->has('_flash') ? $session->get('_flash') : [],
                    [
                        'name'       => 'Flashes',
                        'empty_text' => 'No flash messages were created',
                    ]
                ),
            ],
        ]);
    }

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

    /**
     * Prepare request and response cookie infos and create a cookie tab.
     *
     * @param \Psr\Http\Message\ServerRequestInterface $serverRequest
     * @param \Psr\Http\Message\ResponseInterface      $response
     *
     * @return array
     */
    protected function createCookieTab(ServerRequestInterface $serverRequest, ResponseInterface $response): array
    {
        if (! (\class_exists(RequestCookies::class) && \class_exists(ResponseCookies::class))) {
            return [];
        }

        $requestCookies = $responseCookies = [];

        /** @var Cookie $cookie */
        foreach (RequestCookies::fromRequest($serverRequest)->getAll() as $cookie) {
            $requestCookies[$cookie->getName()] = $cookie->getValue();
        }

        /** @var CookieContract $cookie */
        foreach (ResponseCookies::fromResponse($response)->getAll() as $cookie) {
            $responseCookies[$cookie->getName()] = $cookie->getValue();
        }

        return [
            'name'    => 'Cookies',
            'content' => $this->createTable(
                $requestCookies,
                [
                    'name'       => 'Request Cookies',
                    'empty_text' => 'No request cookies',
                ]
            ) . $this->createTable(
                $responseCookies,
                [
                    'name'       => 'Response Cookies',
                    'empty_text' => 'No response cookies',
                ]
            ),
        ];
    }

    /**
     * Get the route information for a given route.
     *
     * @param \Viserio\Component\Contract\Routing\Route $route
     *
     * @return array
     */
    protected function getRouteInformation(RouteContract $route): array
    {
        $routesPath = \realpath($this->routeDirPath);
        $action     = $route->getAction();
        $reflector  = null;

        $result = [
            'uri'     => $route->getUri() ?: '-',
            'methods' => \count($route->getMethods()) > 1 ?
                 \implode(' | ', $route->getMethods()) :
                 $route->getMethods()[0],
        ];

        $result = \array_merge($result, $action);

        if (isset($action['controller']) && \mb_strpos($action['controller'], '@') !== false) {
            [$controller, $method] = \explode('@', $action['controller']);

            if (\class_exists($controller) && \method_exists($controller, $method)) {
                $reflector = new ReflectionMethod($controller, $method);
            }

            unset($result['uses']);
        } elseif (isset($action['uses']) && $action['uses'] instanceof Closure) {
            $reflector      = new ReflectionFunction($action['uses']);
            $result['uses'] = $this->cloneVar($result['uses']);
        }

        if ($reflector !== null) {
            $filename       = \ltrim(\str_replace($routesPath, '', $reflector->getFileName()), '/');
            $result['file'] = $filename . ': ' . $reflector->getStartLine() . ' - ' . $reflector->getEndLine();
        }

        $result['middleware']         = \implode(', ', $route->gatherMiddleware());
        $result['without_middleware'] = \implode(', ', $route->gatherDisabledMiddleware());

        return $result;
    }

    /**
     * Prepare request attributes, check of route object.
     *
     * @param array $attributes
     *
     * @return array
     */
    protected function prepareRequestAttributes(array $attributes): array
    {
        $preparedAttributes = [];

        foreach ($attributes as $key => $value) {
            if ($key === '_route') {
                if (\is_object($value) && $value instanceof RouteContract) {
                    /** @var RouteContract $route */
                    $route = $value;
                    $value = [
                        'Uri'        => $route->getUri(),
                        'Parameters' => $route->getParameters(),
                    ];
                }

                $preparedAttributes[$key] = $value;
            } elseif ($value instanceof StoreContract) {
                $preparedAttributes[$key] = $value->getId();
            } else {
                $preparedAttributes[$key] = $value;
            }
        }

        return $preparedAttributes;
    }

    /**
     * Prepare request headers.
     *
     * @param array $headers
     *
     * @return array
     */
    protected function prepareRequestHeaders(array $headers): array
    {
        $preparedHeaders = [];

        foreach ($headers as $key => $value) {
            if (\count((array) $value) === 1) {
                $preparedHeaders[$key] = $value[0];
            } else {
                $preparedHeaders[$key] = $value;
            }
        }

        return $preparedHeaders;
    }

    /**
     * Prepare server parameter.
     * Hide all keys with a _KEY|_PASSWORD|_PW|_SECRET in it.
     *
     * @param array $params
     *
     * @return array
     */
    protected function prepareServerParams(array $params): array
    {
        $preparedParams = [];

        foreach ($params as $key => $value) {
            if (\preg_match('/(_KEY|_PASSWORD|_PW|_SECRET)/', $key)) {
                $preparedParams[$key] = '******';
            } else {
                $preparedParams[$key] = $value;
            }
        }

        return $preparedParams;
    }

    /**
     * Post Parameters from parsed body.
     *
     * @param \Psr\Http\Message\ServerRequestInterface $request
     *
     * @return array
     */
    private function getParsedBody(ServerRequestInterface $request): array
    {
        $parsedBody = $request->getParsedBody();

        if (\is_object($parsedBody)) {
            return (array) $parsedBody;
        }

        if ($parsedBody === null) {
            return [];
        }

        return $parsedBody;
    }
}


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

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

class IniDumper implements DumperContract
{
    /**
     * {@inheritdoc}
     */
    public function dump(array $data): string
    {
        $output = '';

        foreach ($data as $section => $array) {
            $output .= $this->writeSection($section, $array);
        }

        return $output;
    }

    /**
     * @param string $section
     * @param array  $array
     *
     * @return string
     */
    protected function writeSection(string $section, array $array): string
    {
        $subsections = [];
        $output      = '[' . $section . ']' . \PHP_EOL;

        foreach ($array as $key => $value) {
            if (\is_array($value) || \is_object($value)) {
                $subsections[$key] = \is_array($value) ? $value : (array) $value;
            } else {
                $output .= \str_replace('=', '_', $key) . '=';
                $output .= self::export($value);
                $output .= \PHP_EOL;
            }
        }

        if (! empty($subsections)) {
            $output .= \PHP_EOL;

            foreach ($subsections as $subsection => $data) {
                if (\is_array($data)) {
                    foreach ($data as $key => $value) {
                        $output .= $subsection . '[' . (\is_string($key) ? $key : '') . ']=' . self::export($value);
                    }
                } else {
                    $output .= $subsection . '[]=' . $data;
                }
            }
        }

        return $output;
    }

    /**
     * Converts the supplied value into a valid ini representation.
     *
     * @param mixed $value
     *
     * @return string
     */
    private static function export($value): string
    {
        if (null === $value) {
            return 'null';
        }

        if (\is_bool($value)) {
            return $value ? 'true' : 'false';
        }

        if (\is_numeric($value)) {
            return '"' . $value . '"';
        }

        return \sprintf('"%s"', $value);
    }
}


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

use League\Flysystem\AdapterInterface;
use League\Flysystem\Cached\CachedAdapter;
use ParagonIE\Halite\Symmetric\EncryptionKey;
use Viserio\Component\Contract\Cache\Traits\CacheManagerAwareTrait;
use Viserio\Component\Contract\Filesystem\Exception\InvalidArgumentException;
use Viserio\Component\Contract\Filesystem\Filesystem as FilesystemContract;
use Viserio\Component\Contract\OptionsResolver\ProvidesDefaultOptions as ProvidesDefaultOptionsContract;
use Viserio\Component\Filesystem\Cache\CachedFactory;
use Viserio\Component\Filesystem\Encryption\EncryptionWrapper;
use Viserio\Component\Manager\AbstractConnectionManager;

class FilesystemManager extends AbstractConnectionManager implements ProvidesDefaultOptionsContract
{
    use CacheManagerAwareTrait;

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

    /**
     * Get a crypted aware connection instance.
     *
     * @param \ParagonIE\Halite\Symmetric\EncryptionKey $key
     * @param null|string                               $name
     *
     * @return \Viserio\Component\Filesystem\Encryption\EncryptionWrapper
     */
    public function cryptedConnection(EncryptionKey $key, string $name = null): EncryptionWrapper
    {
        return new EncryptionWrapper($this->getConnection($name), $key);
    }

    /**
     * Get the clean flysystem adapter.
     *
     * @param null|string $name
     *
     * @return \League\Flysystem\AdapterInterface
     */
    public function getFlysystemAdapter(string $name = null): AdapterInterface
    {
        return parent::getConnection($name);
    }

    /**
     * {@inheritdoc}
     */
    public function getConnection(string $name = null)
    {
        $name = $name ?? $this->getDefaultConnection();

        if (! isset($this->connections[$name])) {
            $config = $this->getConnectionConfig($name);

            $this->connections[$name] = [
                'connection' => $this->createConnection($config),
                'config'     => $config,
            ];
        }

        return $this->adapt(
            $this->connections[$name]['connection'],
            $this->connections[$name]['config']
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getConnectionConfig(string $name): array
    {
        $config    = parent::getConnectionConfig($name);
        $cacheName = ($config['cache'] ?? false);

        if (\is_string($cacheName)) {
            $config['cache'] = $this->getCacheConfig($cacheName);
        }

        return $config;
    }

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

    /**
     * Get the cache configuration.
     *
     * @param string $name
     *
     * @throws \Viserio\Component\Contract\Filesystem\Exception\InvalidArgumentException
     *
     * @return array
     */
    protected function getCacheConfig(string $name): array
    {
        $cache = $this->resolvedOptions['cached'];

        if (! \is_array($config = ($cache[$name] ?? false)) && ! $config) {
            throw new InvalidArgumentException(\sprintf('Cache [%s] not configured.', $name));
        }

        $config['name'] = $name;

        return $config;
    }

    /**
     * Adapt the filesystem implementation.
     *
     * @param \League\Flysystem\AdapterInterface $adapter
     * @param array                              $config
     *
     * @return \Viserio\Component\Contract\Filesystem\Filesystem
     */
    protected function adapt(AdapterInterface $adapter, array $config): FilesystemContract
    {
        if (isset($config['cache']) && \is_array($config['cache'])) {
            $cacheFactory = new CachedFactory($this, $this->getCacheManager());

            $adapter = new CachedAdapter($adapter, $cacheFactory->getConnection($config));

            unset($config['cache']);
        }

        return new FilesystemAdapter($adapter, $config);
    }

    /**
     * Create an instance of the Awss3 connection.
     *
     * @param array $config
     *
     * @throws \Viserio\Component\Contract\Filesystem\Exception\InvalidArgumentException
     *
     * @return \League\Flysystem\AdapterInterface
     */
    protected function createAwss3Connection(array $config): AdapterInterface
    {
        return (new Adapter\AwsS3Connector($config))->connect();
    }

    /**
     * Create an instance of the Dropbox connection.
     *
     * @param array $config
     *
     * @throws \Viserio\Component\Contract\Filesystem\Exception\InvalidArgumentException
     *
     * @return \League\Flysystem\AdapterInterface
     */
    protected function createDropboxConnection(array $config): AdapterInterface
    {
        return (new Adapter\DropboxConnector($config))->connect();
    }

    /**
     * Create an instance of the Ftp connection.
     *
     * @param array $config
     *
     * @throws \Viserio\Component\Contract\Filesystem\Exception\InvalidArgumentException
     *
     * @return \League\Flysystem\AdapterInterface
     */
    protected function createFtpConnection(array $config): AdapterInterface
    {
        return (new Adapter\FtpConnector($config))->connect();
    }

    /**
     * Create an instance of the Local connection.
     *
     * @param array $config
     *
     * @throws \Viserio\Component\Contract\Filesystem\Exception\InvalidArgumentException
     *
     * @return \League\Flysystem\AdapterInterface
     */
    protected function createLocalConnection(array $config): AdapterInterface
    {
        return (new Adapter\LocalConnector($config))->connect();
    }

    /**
     * Create an instance of the Null connection.
     *
     * @param array $config
     *
     * @throws \Viserio\Component\Contract\Filesystem\Exception\InvalidArgumentException
     *
     * @return \League\Flysystem\AdapterInterface
     */
    protected function createNullConnection(array $config): AdapterInterface
    {
        return (new Adapter\NullConnector())->connect();
    }

    /**
     * Create an instance of the Sftp connection.
     *
     * @param array $config
     *
     * @throws \Viserio\Component\Contract\Filesystem\Exception\InvalidArgumentException
     *
     * @return \League\Flysystem\AdapterInterface
     */
    protected function createSftpConnection(array $config): AdapterInterface
    {
        return (new Adapter\SftpConnector($config))->connect();
    }

    /**
     * Create an instance of the Vfs connection.
     *
     * @param array $config
     *
     * @throws \Viserio\Component\Contract\Filesystem\Exception\InvalidArgumentException
     *
     * @return \League\Flysystem\AdapterInterface
     */
    protected function createVfsConnection(array $config): AdapterInterface
    {
        return (new Adapter\VfsConnector())->connect();
    }

    /**
     * Create an instance of the WebDav connection.
     *
     * @param array $config
     *
     * @throws \Viserio\Component\Contract\Filesystem\Exception\InvalidArgumentException
     *
     * @return \League\Flysystem\AdapterInterface
     */
    protected function createWebdavConnection(array $config): AdapterInterface
    {
        return (new Adapter\WebDavConnector($config))->connect();
    }

    /**
     * Create an instance of the Zip connection.
     *
     * @param array $config
     *
     * @throws \Viserio\Component\Contract\Filesystem\Exception\InvalidArgumentException
     *
     * @return \League\Flysystem\AdapterInterface
     */
    protected function createZipConnection(array $config): AdapterInterface
    {
        return (new Adapter\ZipConnector($config))->connect();
    }
}


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

use Viserio\Component\Contract\Routing\Exception\InvalidRoutePatternException;
use Viserio\Component\Contract\Routing\Pattern;
use Viserio\Component\Routing\Matcher\ParameterMatcher;
use Viserio\Component\Routing\Matcher\StaticMatcher;

final class Parser
{
    private const STATIC_PART    = 0;
    private const PARAMETER_PART = 1;

    /**
     * Parses the supplied route pattern into an array of route segments.
     *
     * Example: '/user/{id}/create'
     * Should return: [
     *     \Viserio\Component\Routing\Matcher\StaticMatcher{ $value => 'user' },
     *     \Viserio\Component\Routing\Matcher\ParameterMatcher{ $name => 'id', $match => '[0-9]+' },
     *     \Viserio\Component\Routing\Matcher\StaticMatcher{ $value => 'create' },
     * ]
     *
     * @param string   $route
     * @param string[] $conditions
     *
     * @throws \Viserio\Component\Contract\Routing\Exception\InvalidArgumentException
     * @throws \Viserio\Component\Contract\Routing\Exception\InvalidRoutePatternException
     *
     * @return array
     */
    public static function parse(string $route, array $conditions): array
    {
        if (\strlen($route) > 1 && $route[0] !== '/') {
            throw new InvalidRoutePatternException(\sprintf(
                'Invalid route pattern: non-root route must be prefixed with \'/\', \'%s\' given.',
                $route
            ));
        }

        $segments        = [];
        $matches         = [];
        $names           = [];
        $patternSegments = \explode('/', $route);

        \array_shift($patternSegments);

        foreach ($patternSegments as $key => $patternSegment) {
            if (self::matchRouteParameters($route, $patternSegment, $conditions, $matches, $names)) {
                $segments[] = new ParameterMatcher(
                    $names,
                    self::generateRegex($matches, $conditions)
                );
            } else {
                $segments[] = new StaticMatcher($patternSegment);
            }
        }

        return $segments;
    }

    /**
     * Validate and match uri parameters.
     *
     * @param string $route
     * @param string $patternSegment
     * @param array  $conditions
     * @param array  $matches
     * @param array  $names
     *
     * @throws \Viserio\Component\Contract\Routing\Exception\InvalidRoutePatternException
     *
     * @return bool
     */
    private static function matchRouteParameters(
        string $route,
        string $patternSegment,
        array &$conditions,
        array &$matches,
        array &$names
    ): bool {
        $matchedParameter = false;
        $names            = [];
        $matches          = [];
        $current          = '';
        $inParameter      = false;

        foreach (\str_split($patternSegment) as $character) {
            if ($inParameter) {
                if ($character === '}') {
                    if (\strpos($current, ':') !== false) {
                        $regex                = \substr($current, \strpos($current, ':') + 1);
                        $current              = \substr($current, 0, \strpos($current, ':'));
                        $conditions[$current] = $regex;
                    }

                    $matches[]        = [self::PARAMETER_PART, $current];
                    $names[]          = $current;
                    $current          = '';
                    $inParameter      = false;
                    $matchedParameter = true;

                    continue;
                }

                if ($character === '{') {
                    throw new InvalidRoutePatternException(\sprintf(
                        'Invalid route uri; Cannot contain nested \'{\', \'%s\' given.',
                        $route
                    ));
                }
            } else {
                if ($character === '{') {
                    $matches[]   = [self::STATIC_PART, $current];
                    $current     = '';
                    $inParameter = true;

                    continue;
                }

                if ($character === '}') {
                    throw new InvalidRoutePatternException(\sprintf(
                        'Invalid route uri; Cannot contain \'}\' before opening \'{\', \'%s\' given.',
                        $route
                    ));
                }
            }

            $current .= $character;
        }

        if ($inParameter) {
            throw new InvalidRoutePatternException(\sprintf(
                'Invalid route uri: cannot contain \'{\' without closing \'}\', \'%s\' given',
                $route
            ));
        }

        if ($current !== '') {
            $matches[] = [self::STATIC_PART, $current];
        }

        return $matchedParameter;
    }

    /**
     * Generate a segment regex.
     *
     * @param array $matches
     * @param array $parameterPatterns
     *
     * @return string
     */
    private static function generateRegex(array $matches, array $parameterPatterns): string
    {
        $regex = '/^';

        foreach ($matches as $match) {
            [$type, $part] = $match;

            if ($type === self::STATIC_PART) {
                $regex .= \preg_quote($part, '/');
            } else {
                // Parameter, $part is the parameter name
                $pattern = $parameterPatterns[$part] ?? Pattern::ANY;
                $regex .= '(' . $pattern . ')';
            }
        }

        return $regex . '$/';
    }
}


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

use Invoker\Invoker as DiInvoker;
use Invoker\InvokerInterface;
use Invoker\ParameterResolver\AssociativeArrayResolver;
use Invoker\ParameterResolver\Container\ParameterNameContainerResolver;
use Invoker\ParameterResolver\Container\TypeHintContainerResolver;
use Invoker\ParameterResolver\DefaultValueResolver;
use Invoker\ParameterResolver\NumericArrayResolver;
use Invoker\ParameterResolver\ParameterResolver;
use Invoker\ParameterResolver\ResolverChain;
use Invoker\ParameterResolver\TypeHintResolver;
use Viserio\Component\Contract\Container\Traits\ContainerAwareTrait;

final class Invoker implements InvokerInterface
{
    use ContainerAwareTrait;

    /**
     * Inject settings.
     *
     * @var array
     */
    private $inject = [];

    /**
     * Array of all added resolvers.
     *
     * @var array
     */
    private $resolvers = [];

    /**
     * Invoker instance.
     *
     * @var InvokerInterface
     */
    private $invoker;

    /**
     * Inject by type hint.
     *
     * @param bool $inject
     *
     * @return $this
     */
    public function injectByTypeHint(bool $inject = false): self
    {
        $this->inject['type'] = $inject;

        return $this;
    }

    /**
     * Inject by parameter.
     *
     * @param bool $inject
     *
     * @return $this
     */
    public function injectByParameterName(bool $inject = false): self
    {
        $this->inject['parameter'] = $inject;

        return $this;
    }

    /**
     * Adds a resolver to the invoker class.
     *
     * @param ParameterResolver $resolver
     *
     * @return $this
     */
    public function addResolver(ParameterResolver $resolver): self
    {
        $this->resolvers[] = $resolver;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function call($callable, array $parameters = [])
    {
        return $this->getInvoker()->call($callable, $parameters);
    }

    /**
     * Get a full configured invoker class.
     *
     * @return \Invoker\InvokerInterface
     */
    private function getInvoker(): InvokerInterface
    {
        if ($this->invoker === null) {
            $resolvers = \array_merge([
                new AssociativeArrayResolver(),
                new NumericArrayResolver(),
                new TypeHintResolver(),
                new DefaultValueResolver(),
            ], $this->resolvers);

            if (($container = $this->container) !== null) {
                if (isset($this->inject['type'])) {
                    $resolvers[] = new TypeHintContainerResolver($container);
                }

                if (isset($this->inject['parameter'])) {
                    $resolvers[] = new ParameterNameContainerResolver($container);
                }

                $this->invoker = new DiInvoker(new ResolverChain($resolvers), $container);
            } else {
                $this->invoker = new DiInvoker(new ResolverChain($resolvers));
            }
        }

        return $this->invoker;
    }
}


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

trait ExistTrait
{
    /**
     * Checks various object types for existence.
     *
     * @param mixed $object
     * @param bool  $autoload
     *
     * @return bool
     */
    protected function exists($object, bool $autoload = true): bool
    {
        return \class_exists($object, $autoload)  ||
            \interface_exists($object, $autoload) ||
            \trait_exists($object, $autoload);
    }
}


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

use DOMDocument;
use Viserio\Component\Parser\Utils\XliffUtils;

/**
 * Validates XLIFF files syntax and outputs encountered errors.
 *
 * Some of this code has been ported from Symfony. The original
 *
 * @see https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Translation/Command/XliffLintCommand.php
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 */
class XliffLintCommand extends AbstractLintCommand
{
    /**
     * {@inheritdoc}
     */
    protected static $defaultName = 'lint:xliff';

    /**
     * {@inheritdoc}
     */
    protected $signature = 'lint:xliff
        [filename : A file or a directory or STDIN.]
        [--format=txt : The output format. Supports `txt` or `json`.]
    ';

    /**
     * {@inheritdoc}
     */
    protected $description = 'Lints a XLIFF file and outputs encountered errors.';

    /**
     * {@inheritdoc}
     */
    protected function validate(string $content, ?string $file = null): array
    {
        // Avoid: Warning DOMDocument::loadXML(): Empty string supplied as input
        if (\trim($content) === '') {
            return ['file' => $file, 'valid' => true];
        }

        \libxml_use_internal_errors(true);

        $document = new DOMDocument();
        $document->loadXML($content);

        $errors         = [];
        $targetLanguage = $this->getTargetLanguageFromFile($document);

        if ($targetLanguage !== null) {
            $expectedFileExtension = \sprintf('%s.xlf', \str_replace('-', '_', $targetLanguage));
            $realFileExtension     = \explode('.', \basename($file), 2)[1] ?? '';

            if ($expectedFileExtension !== $realFileExtension) {
                $errors[] = [
                    'line'    => -1,
                    'column'  => -1,
                    'message' => \sprintf('There is a mismatch between the file extension [%s] and the [%s] value used in the "target-language" attribute of the file.', $realFileExtension, $targetLanguage),
                ];
            }
        }

        foreach (XliffUtils::validateSchema($document) as $xmlError) {
            $errors[] = [
                'line'    => $xmlError['line'],
                'column'  => $xmlError['column'],
                'message' => $xmlError['message'],
            ];
        }

        return ['file' => $file, 'valid' => 0 === \count($errors), 'messages' => $errors];
    }

    /**
     * {@inheritdoc}
     */
    protected function displayTxt(array $filesInfo, bool $displayCorrectFiles): int
    {
        $countFiles   = \count($filesInfo);
        $erroredFiles = 0;
        $output       = $this->getOutput();

        foreach ($filesInfo as $info) {
            if ($displayCorrectFiles && $info['valid']) {
                $output->comment('<info>OK</info>' . ($info['file'] ? \sprintf(' in %s', $info['file']) : ''));
            } elseif (! $info['valid']) {
                $erroredFiles++;

                $output->text('<error>ERROR</error>' . ($info['file'] ? \sprintf(' in %s', $info['file']) : ''));

                $output->listing(\array_map(static function ($error) {
                    // general document errors have a '-1' line number
                    return $error['line'] === -1 ? $error['message'] : \sprintf('Line %d, Column %d: %s', $error['line'], $error['column'], $error['message']);
                }, $info['messages']));
            }
        }

        if ($erroredFiles === 0) {
            $this->getOutput()->success(\sprintf('All %d XLIFF files contain valid syntax.', $countFiles));
        } else {
            $this->warn(\sprintf('%d XLIFF files have valid syntax and %d contain errors.', $countFiles - $erroredFiles, $erroredFiles));
        }

        return \min($erroredFiles, 1);
    }

    /**
     * Get the target language from file.
     *
     * @param \DOMDocument $xliffContents
     *
     * @return null|string
     */
    private function getTargetLanguageFromFile(DOMDocument $xliffContents): ?string
    {
        foreach ($xliffContents->getElementsByTagName('file')[0]->attributes ?? [] as $attribute) {
            if ($attribute->nodeName === 'target-language') {
                return $attribute->nodeValue;
            }
        }

        return null;
    }
}


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

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\Cron\Cron;
use Viserio\Component\Cron\Schedule;
use Viserio\Component\OptionsResolver\Traits\OptionsResolverTrait;

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

    /**
     * {@inheritdoc}
     */
    protected static $defaultName = 'cron:run';

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

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

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

    /**
     * {@inheritdoc}
     *
     * @param \Viserio\Component\Cron\Schedule $schedule
     */
    public function handle(Schedule $schedule): int
    {
        $options   = self::resolveOptions($this->getContainer()->get('config'));
        $cronJobs  = $schedule->dueCronJobs(
            $options['env'],
            $options['maintenance']
        );

        $cronJobsRan = 0;

        /** @var Cron $cronJob */
        foreach ($cronJobs as $cronJob) {
            if (! $cronJob->filtersPass()) {
                continue;
            }

            $this->line('<info>Running scheduled command:</info> ' . $cronJob->getSummaryForDisplay());

            $cronJob->run();

            $cronJobsRan++;
        }

        if (\count($cronJobs) === 0 || $cronJobsRan === 0) {
            $this->info('No scheduled commands are ready to run.');
        }

        return 0;
    }
}


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

use Interop\Container\ServiceProviderInterface;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;
use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider;
use Symfony\Component\VarDumper\Server\Connection;
use Symfony\Component\VarDumper\Server\DumpServer;
use Symfony\Component\VarDumper\VarDumper;
use Viserio\Component\Contract\OptionsResolver\ProvidesDefaultOptions as ProvidesDefaultOptionsContract;
use Viserio\Component\Contract\OptionsResolver\RequiresComponentConfig as RequiresComponentConfigContract;
use Viserio\Component\OptionsResolver\Traits\OptionsResolverTrait;
use Viserio\Component\WebServer\RequestContextProvider;

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

    /**
     * {@inheritdoc}
     */
    public function getFactories(): array
    {
        return [
            RequestContextProvider::class => [self::class, 'createRequestContextProvider'],
            Connection::class             => [self::class, 'createVarDumpConnection'],
            DumpServer::class             => [self::class, 'createDumpServer'],
        ];
    }

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

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

    /**
     * {@inheritdoc}
     */
    public static function getDefaultOptions(): array
    {
        return [
            'debug_server' => [
                'host' => 'tcp://127.0.0.1:9912',
            ],
        ];
    }

    /**
     * Create a new RequestContextProvider instance.
     *
     * @param \Psr\Container\ContainerInterface $container
     *
     * @return \Viserio\Component\WebServer\RequestContextProvider
     */
    public static function createRequestContextProvider(ContainerInterface $container): RequestContextProvider
    {
        return new RequestContextProvider($container->get(ServerRequestInterface::class));
    }

    /**
     * Create a new VarDump Connection instance.
     *
     * @param \Psr\Container\ContainerInterface $container
     *
     * @return \Symfony\Component\VarDumper\Server\Connection
     */
    public static function createVarDumpConnection(ContainerInterface $container): Connection
    {
        $resolvedOptions  = self::resolveOptions($container->get('config'));
        $contextProviders = [];

        if ($container->has(ServerRequestInterface::class) && $container->has(RequestContextProvider::class)) {
            $contextProviders['request'] = $container->get(RequestContextProvider::class);
        }

        if ($container->has(SourceContextProvider::class)) {
            $contextProviders['source'] = $container->get(SourceContextProvider::class);
        }

        return new Connection(
            $resolvedOptions['debug_server']['host'],
            $contextProviders
        );
    }

    /**
     * Create a new DumpServer instance.
     *
     * @param \Psr\Container\ContainerInterface $container
     *
     * @return \Symfony\Component\VarDumper\Server\DumpServer
     */
    public static function createDumpServer(ContainerInterface $container): DumpServer
    {
        $connection = $container->get(Connection::class);
        // @codeCoverageIgnoreStart
        VarDumper::setHandler(static function ($var) use ($connection): void {
            $data = (new VarCloner())->cloneVar($var);

            if ($connection->write($data)) {
                (new CliDumper())->dump($data);
            }
        });
        // @codeCoverageIgnoreEnd
        $logger = null;

        if ($container->has(LoggerInterface::class)) {
            $logger = $container->get(LoggerInterface::class);
        }

        $resolvedOptions = self::resolveOptions($container->get('config'));

        return new DumpServer(
            $resolvedOptions['debug_server']['host'],
            $logger
        );
    }
}


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

use League\Flysystem\Adapter\Local as LocalAdapter;
use League\Flysystem\AdapterInterface;
use League\Flysystem\AwsS3v3\AwsS3Adapter;
use League\Flysystem\Config as FlyConfig;
use RuntimeException;
use Spatie\Macroable\Macroable;
use Viserio\Component\Contract\Filesystem\Exception\FileNotFoundException;
use Viserio\Component\Contract\Filesystem\Exception\InvalidArgumentException;
use Viserio\Component\Contract\Filesystem\Exception\IOException;
use Viserio\Component\Contract\Filesystem\Exception\IOException as ViserioIOException;
use Viserio\Component\Contract\Filesystem\Filesystem as FilesystemContract;
use Viserio\Component\Filesystem\Traits\FilesystemExtensionTrait;
use Viserio\Component\Filesystem\Traits\FilesystemHelperTrait;

class FilesystemAdapter implements FilesystemContract
{
    use FilesystemExtensionTrait;
    use FilesystemHelperTrait;
    use Macroable;

    /**
     * The Flysystem filesystem implementation.
     *
     * @var \League\Flysystem\AdapterInterface
     */
    protected $driver;

    /**
     * Adapter configs.
     *
     * @var array
     */
    protected $config;

    /**
     * Create a new filesystem adapter instance.
     *
     * @param \League\Flysystem\AdapterInterface $driver
     * @param array                              $config
     */
    public function __construct(AdapterInterface $driver, array $config)
    {
        $this->driver = $driver;
        $this->config = $config;
    }

    /**
     * Call a Flysystem driver plugin.
     *
     * @param string $method
     * @param array  $arguments
     *
     * @throws \BadMethodCallException
     *
     * @return mixed
     */
    public function __call(string $method, array $arguments)
    {
        return $this->driver->{$method}(...$arguments);
    }

    /**
     * Get the Flysystem driver.
     *
     * @return \League\Flysystem\AdapterInterface
     */
    public function getDriver(): AdapterInterface
    {
        return $this->driver;
    }

    /**
     * Check whether a file exists.
     *
     * @param string $path
     *
     * @return bool
     */
    public function has(string $path): bool
    {
        $has = $this->driver->has($path);

        if ($has === null) {
            return false;
        }

        if (\is_array($has)) {
            return $has['path'] !== '';
        }

        return $has;
    }

    /**
     * {@inheritdoc}
     */
    public function read(string $path)
    {
        if (! $this->has($path)) {
            throw new FileNotFoundException($path);
        }

        $content = $this->driver->read($path);

        if ($content !== false) {
            return $content['contents'];
        }

        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function readStream(string $path)
    {
        if (! $this->has($path)) {
            throw new FileNotFoundException($path);
        }

        $content = $this->driver->readStream($path);

        if ($content !== false) {
            return $content['stream'];
        }

        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function write(string $path, string $contents, array $config = []): bool
    {
        $config['visibility'] = $this->parseVisibility($config['visibility'] ?? null) ?: [];

        $flyConfig = new FlyConfig($config);

        return $this->driver->write($path, $contents, $flyConfig) !== false;
    }

    /**
     * {@inheritdoc}
     */
    public function writeStream(string $path, $resource, array $config = []): bool
    {
        $config['visibility'] = $this->parseVisibility($config['visibility'] ?? null) ?: [];

        $flyConfig = new FlyConfig($config);

        return $this->driver->writeStream($path, $resource, $flyConfig) !== false;
    }

    /**
     * {@inheritdoc}
     */
    public function put(string $path, $contents, array $config = []): bool
    {
        $config['visibility'] = $this->parseVisibility($config['visibility'] ?? null) ?: [];

        $flyConfig = new FlyConfig($config);

        if (\is_resource($contents)) {
            if ($this->has($path)) {
                return $this->driver->updateStream($path, $contents, $flyConfig) !== false;
            }

            return $this->driver->writeStream($path, $contents, $flyConfig) !== false;
        }

        if ($this->has($path)) {
            return $this->driver->update($path, $contents, $flyConfig) !== false;
        }

        return $this->driver->write($path, $contents, $flyConfig) !== false;
    }

    /**
     * {@inheritdoc}
     *
     * @throws \Viserio\Component\Contract\Filesystem\Exception\IOException If file cant be deleted
     */
    public function append(string $path, string $contents, array $config = []): bool
    {
        if ($this->has($path)) {
            $contents = $this->read($path) . $contents;

            if (! $this->driver->delete($path)) {
                throw new IOException(\sprintf('File [%s] cannot be deleted.', $path));
            }
        }

        return $this->write($path, $contents, $config);
    }

    /**
     * {@inheritdoc}
     */
    public function appendStream(string $path, $resource, array $config = []): bool
    {
        if ($this->has($path)) {
            return $this->updateStream($path, $resource, $config);
        }

        return $this->writeStream($path, $resource, $config);
    }

    /**
     * {@inheritdoc}
     */
    public function update(string $path, string $contents, array $config = []): bool
    {
        if (! $this->has($path)) {
            throw new FileNotFoundException($path);
        }

        $flyConfig = new FlyConfig($config);

        return $this->driver->update($path, $contents, $flyConfig) !== false;
    }

    /**
     * {@inheritdoc}
     */
    public function updateStream(string $path, $resource, array $config = []): bool
    {
        $config['visibility'] = $this->parseVisibility($config['visibility'] ?? null) ?: [];

        $flyConfig = new FlyConfig($config);

        return $this->driver->updateStream($path, $resource, $flyConfig) !== false;
    }

    /**
     * {@inheritdoc}
     */
    public function getVisibility(string $path): string
    {
        $visibility = $this->driver->getVisibility($path);

        if (isset($visibility['visibility']) && $visibility['visibility'] === AdapterInterface::VISIBILITY_PUBLIC) {
            return FilesystemContract::VISIBILITY_PUBLIC;
        }

        return FilesystemContract::VISIBILITY_PRIVATE;
    }

    /**
     * {@inheritdoc}
     */
    public function setVisibility(string $path, string $visibility): bool
    {
        return $this->driver->setVisibility(
            $path,
            $this->parseVisibility($visibility)
        ) !== false;
    }

    /**
     * {@inheritdoc}
     */
    public function copy($originFile, $targetFile, $override = false): bool
    {
        if (! $this->has($originFile)) {
            throw new FileNotFoundException($originFile);
        }

        if (\method_exists($this->driver, 'applyPathPrefix')) {
            $orginal = $this->driver->applyPathPrefix($originFile);
            $target  = $this->driver->applyPathPrefix($targetFile);
        } else {
            $orginal = $originFile;
            $target  = $targetFile;
        }

        // https://bugs.php.net/bug.php?id=64634
        if (@\fopen($orginal, 'r') === false) {
            throw new ViserioIOException(\sprintf(
                'Failed to copy [%s] to [%s] because source file could not be opened for reading.',
                $orginal,
                $target
            ), 0, null, $orginal);
        }

        // Stream context created to allow files overwrite when using FTP stream wrapper - disabled by default
        if (@\fopen($target, 'w', false, \stream_context_create(['ftp' => ['overwrite' => true]])) === false) {
            throw new ViserioIOException(\sprintf(
                'Failed to copy [%s] to [%s] because target file could not be opened for writing.',
                $orginal,
                $target
            ), 0, null, $orginal);
        }

        $this->driver->copy($originFile, $targetFile);

        if (! \is_file($target)) {
            throw new ViserioIOException(\sprintf(
                'Failed to copy [%s] to [%s].',
                $originFile,
                $target
            ), 0, null, $originFile);
        }

        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function move(string $from, string $to): bool
    {
        return $this->driver->rename($from, $to);
    }

    /**
     * {@inheritdoc}
     */
    public function getSize(string $path)
    {
        $size = $this->driver->getSize($path);

        if ($size !== false) {
            return $size['size'];
        }

        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function getMimetype(string $path)
    {
        if (! $this->has($path)) {
            throw new FileNotFoundException($path);
        }

        $mimetype = $this->driver->getMimetype($path);

        if ($mimetype !== false) {
            return $mimetype['mimetype'];
        }

        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function getTimestamp(string $path)
    {
        $this->fileNotFound($path);

        $getTimestamp = $this->driver->getTimestamp($path);

        if ($getTimestamp !== false) {
            return $getTimestamp['timestamp'];
        }

        return false;
    }

    /**
     * {@inheritdoc}
     *
     * @throws \RuntimeException
     */
    public function url(string $path): string
    {
        $adapter = $this->driver;

        if ($adapter instanceof AwsS3Adapter) {
            $path = $adapter->getPathPrefix() . $path;

            return $adapter->getClient()->getObjectUrl($adapter->getBucket(), $path);
        }

        if ($adapter instanceof LocalAdapter) {
            if (isset($this->config['url'])) {
                return$this->config['url'] . \DIRECTORY_SEPARATOR . $path;
            }

            return $adapter->getPathPrefix() . $path;
        }

        if (\method_exists($adapter, 'getUrl')) {
            return $adapter->getUrl($path);
        }

        throw new RuntimeException('This driver does not support retrieving URLs.');
    }

    /**
     * {@inheritdoc}
     */
    public function delete($paths): bool
    {
        $paths   = (array) $paths;
        $success = true;

        foreach ($paths as $path) {
            try {
                if (! $this->driver->delete($path)) {
                    $success = false;
                }
            } catch (FileNotFoundException $e) {
                $success = false;
            }
        }

        return $success;
    }

    /**
     * {@inheritdoc}
     */
    public function files(string $directory): array
    {
        return $this->getContents($directory, 'file');
    }

    /**
     * {@inheritdoc}
     */
    public function allFiles(string $directory, bool $showHiddenFiles = false): array
    {
        return $this->getContents($directory, 'file', true);
    }

    /**
     * {@inheritdoc}
     */
    public function directories(string $directory): array
    {
        return $this->getContents($directory, 'dir');
    }

    /**
     * {@inheritdoc}
     */
    public function allDirectories(string $directory): array
    {
        return $this->getContents($directory, 'dir', true);
    }

    /**
     * {@inheritdoc}
     */
    public function createDirectory(string $path, array $config = []): bool
    {
        return $this->driver->createDir($path, new FlyConfig($config)) !== false;
    }

    /**
     * {@inheritdoc}
     */
    public function deleteDirectory(string $directory): bool
    {
        return $this->driver->deleteDir($directory);
    }

    /**
     * {@inheritdoc}
     */
    public function cleanDirectory(string $dirname): bool
    {
        if (! $this->isDirectory($dirname)) {
            return false;
        }

        $directories = $this->allDirectories($dirname);

        foreach ($directories as $directoryName) {
            @\rmdir($this->getTransformedPath($directoryName));
        }

        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function isDirectory(string $dirname): bool
    {
        return \is_dir($this->getTransformedPath($dirname));
    }

    /**
     * {@inheritdoc}
     */
    public function copyDirectory(string $directory, string $destination, array $options = []): bool
    {
        if (! $this->isDirectory($directory)) {
            return false;
        }

        if (! \is_dir($destination)) {
            $this->createDirectory($destination, ['visibility' => 'public']);
        }

        $recursive = $options['recursive'] ?? true;

        $contents = $this->driver->listContents($directory, $recursive);

        foreach ($contents as $item) {
            if ($item['type'] === 'dir') {
                $this->createDirectory(
                    $destination . \str_replace($directory, '', $item['path']),
                    ['visibility' => $this->getVisibility($item['path'])]
                );
            }

            if ($item['type'] === 'file') {
                $this->copy(
                    $item['path'],
                    $destination . \str_replace($directory, '', $item['path'])
                );
            }
        }

        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function moveDirectory(string $directory, string $destination, array $options = []): bool
    {
        $overwrite = $options['overwrite'] ?? false;

        if ($overwrite && $this->isDirectory($destination) && ! $this->deleteDirectory($destination)) {
            return false;
        }

        $copy = $this->copyDirectory(
            $directory,
            $destination,
            ['visibility' => $this->getVisibility($directory)]
        );
        $delete = $this->deleteDirectory($directory);

        return ! (! $copy && ! $delete);
    }

    /**
     * Get normalize or prefixed path.
     *
     * @param string $path
     *
     * @return string
     */
    protected function getTransformedPath(string $path): string
    {
        $prefix = '';

        if (\method_exists($this->driver, 'getPathPrefix')) {
            $prefix = $this->driver->getPathPrefix();
        }

        return $prefix . $path;
    }

    /**
     * Parse the given visibility value.
     *
     * @param null|string $visibility
     *
     * @throws \Viserio\Component\Contract\Filesystem\Exception\InvalidArgumentException
     *
     * @return null|string
     */
    private function parseVisibility(string $visibility = null): ?string
    {
        if ($visibility === null) {
            return null;
        }

        if ($visibility === FilesystemContract::VISIBILITY_PUBLIC) {
            return AdapterInterface::VISIBILITY_PUBLIC;
        }

        if ($visibility === FilesystemContract::VISIBILITY_PRIVATE) {
            return AdapterInterface::VISIBILITY_PRIVATE;
        }

        throw new InvalidArgumentException('Unknown visibility: ' . $visibility);
    }

    /**
     * Get content from a dir.
     *
     * @param string $directory
     * @param string $typ
     * @param bool   $recursive
     *
     * @return array
     */
    private function getContents(string $directory, string $typ, bool $recursive = false): array
    {
        $contents = $this->driver->listContents($directory, $recursive);

        return $this->filterContentsByType($contents, $typ);
    }

    /**
     * Filter directory contents by type.
     *
     * @param array  $contents
     * @param string $type
     *
     * @return array
     */
    private function filterContentsByType(array $contents, string $type): array
    {
        $results = [];

        foreach ($contents as $key => $value) {
            if (isset($value['type']) && $value['type'] === $type) {
                $results[$key] = $value['path'];
            }
        }

        return $results;
    }

    /**
     * Throws a exception if file is not found.
     *
     * @param string $path
     *
     * @throws \Viserio\Component\Contract\Filesystem\Exception\FileNotFoundException
     *
     * @return void
     */
    private function fileNotFound(string $path): void
    {
        if (! $this->has($path)) {
            throw new FileNotFoundException($path);
        }
    }
}


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

use ParagonIE\Halite\Alerts\InvalidMessage;
use ParagonIE\Halite\KeyFactory;
use ParagonIE\Halite\Symmetric\Crypto;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Viserio\Component\Contract\Session\Exception\SessionNotStartedException;
use Viserio\Component\Contract\Session\Exception\TokenMismatchException;
use Viserio\Component\Contract\Session\Store as StoreContract;
use Viserio\Component\Cookie\SetCookie;
use Viserio\Component\Session\SessionManager;

class VerifyCsrfTokenMiddleware implements MiddlewareInterface
{
    /**
     * The session manager.
     *
     * @var \Viserio\Component\Session\SessionManager
     */
    protected $manager;

    /**
     * Cookie config from session manager.
     *
     * @var array
     */
    protected $cookieConfig = [];

    /**
     * Session cookie lifetime.
     *
     * @var int
     */
    protected $lifetime;

    /**
     * Create a new session middleware.
     *
     * @param \Viserio\Component\Session\SessionManager $manager
     */
    public function __construct(SessionManager $manager)
    {
        $this->manager      = $manager;
        $this->lifetime     = $manager->getConfig()['lifetime'];
        $this->cookieConfig = $manager->getConfig()['cookie'];
    }

    /**
     * {@inheritdoc}
     *
     * @throws \Viserio\Component\Contract\Session\Exception\SessionNotStartedException
     * @throws \Viserio\Component\Contract\Session\Exception\TokenMismatchException
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        if (! $request->getAttribute('session') instanceof StoreContract) {
            throw new SessionNotStartedException('The session is not started.');
        }

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

        if ($this->isReading($request) ||
            $this->runningUnitTests() ||
            $this->tokensMatch($request)
        ) {
            return $this->addCookieToResponse($request, $response);
        }

        throw new TokenMismatchException();
    }

    /**
     * Determine if the application is running unit tests.
     *
     * @return bool
     */
    protected function runningUnitTests(): bool
    {
        return \in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && ($this->manager->getConfig()['env'] ?? 'prod') === 'testing';
    }

    /**
     * Determine if the session and input CSRF tokens match.
     *
     * @param \Psr\Http\Message\ServerRequestInterface $request
     *
     * @return bool
     */
    protected function tokensMatch(ServerRequestInterface $request): bool
    {
        $sessionToken = $request->getAttribute('session')->getToken();
        $token        = $request->getAttribute('_token') ?? $request->getHeaderLine('x-csrf-token');
        $header       = $request->getHeaderLine('x-xsrf-token');

        if ($token === '' && $header !== '') {
            try {
                $key          = KeyFactory::loadEncryptionKey($this->manager->getConfig()['key_path']);
                $hiddenString = Crypto::decrypt($header, $key);
                $token        = $hiddenString->getString();
            } catch (InvalidMessage $exception) {
                $token = $header;
            }
        }

        if (! \is_string($sessionToken) || ! \is_string($token)) {
            return false;
        }

        return \hash_equals($sessionToken, $token);
    }

    /**
     * Add the CSRF token to the response cookies.
     *
     * @param \Psr\Http\Message\ServerRequestInterface $request
     * @param \Psr\Http\Message\ResponseInterface      $response
     *
     * @throws \InvalidArgumentException
     *
     * @return \Psr\Http\Message\ResponseInterface
     */
    protected function addCookieToResponse(
        ServerRequestInterface $request,
        ResponseInterface $response
    ): ResponseInterface {
        $uri = $request->getUri();

        $setCookie = new SetCookie(
            'XSRF-TOKEN',
            $request->getAttribute('session')->getToken(),
            $this->lifetime,
            $this->cookieConfig['path'],
            $this->cookieConfig['domain'] ?? $uri->getHost(),
            $this->cookieConfig['secure'] ?? ($uri->getScheme() === 'https'),
            false,
            $this->cookieConfig['samesite']
        );

        return $response->withAddedHeader('set-cookie', (string) $setCookie);
    }

    /**
     * Determine if the HTTP request uses a ‘read’ verb.
     *
     * @param \Psr\Http\Message\ServerRequestInterface $request
     *
     * @return bool
     */
    protected function isReading(ServerRequestInterface $request): bool
    {
        return \in_array(\mb_strtoupper($request->getMethod()), ['HEAD', 'GET', 'OPTIONS'], true);
    }
}


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

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Viserio\Component\Contract\Profiler\PanelAware as PanelAwareContract;
use Viserio\Component\Contract\Profiler\TooltipAware as TooltipAwareContract;
use Viserio\Component\Contract\Translation\Traits\TranslatorAwareTrait;
use Viserio\Component\Contract\Translation\Translator as TranslatorContract;
use Viserio\Component\Profiler\DataCollector\AbstractDataCollector;

class ViserioTranslationDataCollector extends AbstractDataCollector implements
    TooltipAwareContract,
    PanelAwareContract
{
    use TranslatorAwareTrait;

    /**
     * Create new translation data collector.
     *
     * @param \Viserio\Component\Contract\Translation\Translator $translator
     */
    public function __construct(TranslatorContract $translator)
    {
        $this->translator = $translator;
    }

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

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

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

    /**
     * {@inheritdoc}
     */
    public function getTooltip(): string
    {
        return $this->createTooltipGroup([
            'Missing messages'  => $this->data['counted'][TranslatorContract::MESSAGE_MISSING],
            'Fallback messages' => $this->data['counted'][TranslatorContract::MESSAGE_EQUALS_FALLBACK],
            'Defined messages'  => $this->data['counted'][TranslatorContract::MESSAGE_DEFINED],
        ]);
    }

    /**
     * {@inheritdoc}
     */
    public function getPanel(): string
    {
        $sortedMessages = $this->getSortedMessages($this->data['messages']);

        $tableHeaders = [
            'Locale',
            'Domain',
            'Times used',
            'Message ID',
            'Message Preview',
        ];

        return $this->createTabs([
            [
                'name' => 'Defined ' .
                    $this->data['counted'][TranslatorContract::MESSAGE_DEFINED] .
                    '',
                'content' => $this->createTable(
                    \array_values($sortedMessages[TranslatorContract::MESSAGE_DEFINED]),
                    [
                        'name'      => 'These messages are correctly translated into the given locale.',
                        'headers'   => $tableHeaders,
                        'vardumper' => false,
                    ]
                ),
            ],
            [
                'name' => 'Fallback ' .
                    $this->data['counted'][TranslatorContract::MESSAGE_EQUALS_FALLBACK] .
                    '',
                'content' => $this->createTable(
                    \array_values($sortedMessages[TranslatorContract::MESSAGE_EQUALS_FALLBACK]),
                    [
                        'name'      => 'These messages are not available for the given locale but Narrowspark found them in the fallback locale catalog.',
                        'headers'   => $tableHeaders,
                        'vardumper' => false,
                    ]
                ),
            ],
            [
                'name' => 'Missing ' .
                    $this->data['counted'][TranslatorContract::MESSAGE_MISSING] .
                    '',
                'content' => $this->createTable(
                    \array_values($sortedMessages[TranslatorContract::MESSAGE_MISSING]),
                    [
                        'name' => 'These messages are not available for the given locale and cannot be found in the fallback locales.' .
                        '
 Add them to the translation catalogue to avoid Narrowspark outputting untranslated contents.',
                        'headers'   => $tableHeaders,
                        'vardumper' => false,
                    ]
                ),
            ],
        ]);
    }

    /**
     * Get all collected messages.
     *
     * @return array
     *
     * @codeCoverageIgnore
     */
    public function getMessages(): array
    {
        return $this->data['messages'] ?? [];
    }

    /**
     * Get counted messages.
     *
     * @return array
     *
     * @codeCoverageIgnore
     */
    public function getCountedMessages(): array
    {
        return $this->data['counted'] ?? [];
    }

    /**
     * Sanitize collected messages.
     *
     * @param array $messages
     *
     * @return array
     */
    protected function sanitizeCollectedMessages(array $messages): array
    {
        $result = [];

        foreach ($messages as $key => $message) {
            $messageId = $message['locale'] . '.' . $message['domain'] . '.' . $message['id'];

            if (! isset($result[$messageId])) {
                $message['count']              = 1;
                $message['parameters']         = ! isset($message['parameters']) ? [$message['parameters']] : [];
                $messages[$key]['translation'] = $message['translation'];
                $result[$messageId]            = $message;
            } else {
                if (! isset($message['parameters'])) {
                    $result[$messageId]['parameters'][] = $message['parameters'];
                }

                $result[$messageId]['count']++;
            }
            unset($messages[$key]);
        }

        return $result;
    }

    /**
     * Counter for message types.
     *
     * @param array $messages
     *
     * @return array
     */
    protected function computeCount(array $messages): array
    {
        $count = [
            TranslatorContract::MESSAGE_DEFINED         => 0,
            TranslatorContract::MESSAGE_MISSING         => 0,
            TranslatorContract::MESSAGE_EQUALS_FALLBACK => 0,
        ];

        foreach ($messages as $message) {
            $count[$message['state']]++;
        }

        return $count;
    }

    /**
     * Sorte messages to the right type.
     *
     * @param array $messages
     *
     * @return array
     */
    protected function getSortedMessages(array $messages): array
    {
        $sortedMessages = [
            TranslatorContract::MESSAGE_MISSING         => [],
            TranslatorContract::MESSAGE_EQUALS_FALLBACK => [],
            TranslatorContract::MESSAGE_DEFINED         => [],
        ];

        foreach ($messages as $key => $value) {
            if ($value['state'] === TranslatorContract::MESSAGE_MISSING) {
                $sortedMessages[TranslatorContract::MESSAGE_MISSING][$value['id']] = [
                    $value['locale'],
                    $value['domain'],
                    $value['count'],
                    $value['id'],
                    $value['translation'],
                ];
            } elseif ($value['state'] === TranslatorContract::MESSAGE_EQUALS_FALLBACK) {
                $sortedMessages[TranslatorContract::MESSAGE_EQUALS_FALLBACK][$value['id']] = [
                    $value['locale'],
                    $value['domain'],
                    $value['count'],
                    $value['id'],
                    $value['translation'],
                ];
            } elseif ($value['state'] === TranslatorContract::MESSAGE_DEFINED) {
                $sortedMessages[TranslatorContract::MESSAGE_DEFINED][$value['id']] = [
                    $value['locale'],
                    $value['domain'],
                    $value['count'],
                    $value['id'],
                    $value['translation'],
                ];
            }
        }

        return $sortedMessages;
    }
}


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

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

/**
 * Reads from multiple streams, one after the other.
 *
 * This is a read-only stream decorator.
 */
class AppendStream implements StreamInterface
{
    /** @var \Psr\Http\Message\StreamInterface[] Streams being decorated */
    private $streams = [];

    /**
     * @var bool
     */
    private $seekable = true;

    /**
     * @var int
     */
    private $current = 0;

    /**
     * @var int
     */
    private $pos = 0;

    /**
     * Create a new AppendStream instance.
     *
     * @param \Psr\Http\Message\StreamInterface[] $streams Streams to decorate. Each stream must
     *                                                     be readable.
     */
    public function __construct(array $streams = [])
    {
        foreach ($streams as $stream) {
            $this->addStream($stream);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function __toString(): string
    {
        try {
            $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 '';
        }
    }

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

    /**
     * Add a stream to the AppendStream.
     *
     * @param \Psr\Http\Message\StreamInterface $stream Stream to append. Must be readable.
     *
     * @throws \Viserio\Component\Contract\Http\Exception\InvalidArgumentException if the stream is not readable
     */
    public function addStream(StreamInterface $stream): void
    {
        if (! $stream->isReadable()) {
            throw new InvalidArgumentException('Each stream must be readable.');
        }

        // The stream is only seekable if all streams are seekable
        if (! $stream->isSeekable()) {
            $this->seekable = false;
        }

        $this->streams[] = $stream;
    }

    /**
     * {@inheritdoc}
     */
    public function getContents(): string
    {
        return Util::copyToString($this);
    }

    /**
     * Closes each attached stream.
     *
     * {@inheritdoc}
     */
    public function close(): void
    {
        $this->pos      = $this->current      = 0;
        $this->seekable = true;

        foreach ($this->streams as $stream) {
            $stream->close();
        }

        $this->streams = [];
    }

    /**
     * Detaches each attached stream.
     *
     * Returns null as it's not clear which underlying stream resource to return.
     *
     * {@inheritdoc}
     */
    public function detach(): void
    {
        $this->pos      = $this->current      = 0;
        $this->seekable = true;

        foreach ($this->streams as $stream) {
            $stream->detach();
        }

        $this->streams = [];
    }

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

    /**
     * Tries to calculate the size by adding the size of each stream.
     *
     * If any of the streams do not return a valid number, then the size of the
     * append stream cannot be determined and null is returned.
     *
     * {@inheritdoc}
     */
    public function getSize(): ?int
    {
        $size = 0;

        foreach ($this->streams as $stream) {
            $s = $stream->getSize();

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

            $size += $s;
        }

        return $size;
    }

    /**
     * {@inheritdoc}
     */
    public function eof(): bool
    {
        return ! $this->streams ||
            ($this->current >= \count($this->streams) - 1 &&
                $this->streams[$this->current]->eof());
    }

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

    /**
     * Attempts to seek to the given position. Only supports SEEK_SET.
     *
     * {@inheritdoc}
     */
    public function seek($offset, $whence = \SEEK_SET): void
    {
        if (! $this->seekable) {
            throw new RuntimeException('This AppendStream is not seekable.');
        }

        if ($whence !== \SEEK_SET) {
            throw new RuntimeException('The AppendStream can only seek with SEEK_SET.');
        }

        $this->pos = $this->current = 0;

        // Rewind each stream
        foreach ($this->streams as $i => $stream) {
            try {
                $stream->rewind();
            } catch (Throwable $e) {
                throw new RuntimeException('Unable to seek stream '
                    . $i . ' of the AppendStream', 0, $e);
            }
        }

        // Seek to the actual position by reading from each stream
        while ($this->pos < $offset && ! $this->eof()) {
            $result = $this->read(\min(8096, $offset - $this->pos));

            if ($result === '') {
                break;
            }
        }
    }

    /**
     * Reads from all of the appended streams until the length is met or EOF.
     *
     * {@inheritdoc}
     */
    public function read($length): string
    {
        $buffer         = '';
        $total          = \count($this->streams) - 1;
        $remaining      = $length;
        /** @var bool $progressToNext */
        $progressToNext = false;

        while ($remaining > 0) {
            // Progress to the next stream if needed.
            if ($progressToNext || $this->streams[$this->current]->eof()) {
                $progressToNext = false;

                if ($this->current === $total) {
                    break;
                }

                $this->current++;
            }

            $result = $this->streams[$this->current]->read($remaining);

            // Using a loose comparison here to match on '', false, and null
            if ($result === null) {
                $progressToNext = true;

                continue;
            }

            $buffer .= $result;
            $remaining = $length - \strlen($buffer);
        }

        $this->pos += \strlen($buffer);

        return $buffer;
    }

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

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

    /**
     * {@inheritdoc}
     */
    public function write($string): void
    {
        throw new RuntimeException('Cannot write to an AppendStream.');
    }

    /**
     * {@inheritdoc}
     */
    public function getMetadata($key = null)
    {
        return $key ? null : [];
    }
}


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

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

class ViserioEventsDataCollector extends AbstractDataCollector implements PanelAwareContract
{
    use EventManagerAwareTrait;

    /**
     * Create new events collector instance.
     *
     * @param \Viserio\Component\Events\DataCollector\TraceableEventManager $eventManager
     */
    public function __construct(TraceableEventManager $eventManager)
    {
        $this->eventManager = $eventManager;

        $this->data = [
            'called_listeners'     => [],
            'not_called_listeners' => [],
            'orphaned_events'      => [],
        ];
    }

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

    /**
     * {@inheritdoc}
     */
    public function collect(ServerRequestInterface $serverRequest, ResponseInterface $response): void
    {
        $this->data = [
            'called_listeners'     => $this->eventManager->getCalledListeners(),
            'not_called_listeners' => $this->eventManager->getNotCalledListeners(),
            'orphaned_events'      => $this->eventManager->getOrphanedEvents(),
        ];
    }

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

    /**
     * {@inheritdoc}
     */
    public function getPanel(): string
    {
        $calledContent = $notCalledContent = '';
        $called        = $notCalled        = [];

        $tableConfig = static function (string $name, string $emptyText): array {
            return ['name' => $name, 'headers' => ['Priority', 'Listener'], 'vardumper' => false, 'empty_text' => $emptyText];
        };

        foreach ($this->data['called_listeners'] as $eventName => $calledListener) {
            foreach ($calledListener as $listner) {
                $called[] = [$listner['priority'], $listner['pretty']];
            }

            $calledContent .= $this->createTable($called, $tableConfig($eventName, 'No events have been recorded. Check that debugging is enabled in the kernel.'));
        }

        foreach ($this->data['not_called_listeners'] as $eventName => $calledListener) {
            foreach ($calledListener as $listner) {
                $notCalled[] = [$listner['priority'], $listner['pretty']];
            }

            $notCalledContent .= $this->createTable(
                $notCalled,
                $tableConfig(
                    $eventName,
                    '
There are no uncalled listeners.
' .
                    '
All listeners were called for this request or an error occurred when trying to collect uncalled listeners' .
                    '(in which case check the logs to get more information).
'
                )
            );
        }

        $orphanedEventsContent = $this->createTable(
            $this->data['orphaned_events'],
            [
                'headers'    => ['events'],
                'vardumper'  => false,
                'empty_text' => '
There are no orphaned events.
' .
                    '
All dispatched events were handled or an error occurred when trying to collect orphaned events' .
                    '(in which case check the logs to get more information).
',
            ]
        );

        return $this->createTabs([
            [
                'name'    => 'Called Listeners ' . \count($called) . '',
                'content' => $calledContent,
            ],
            [
                'name'    => 'Not Called Listeners ' . \count($notCalled) . '',
                'content' => $notCalledContent,
            ],
            [
                'name'    => 'Orphaned events ' . \count($this->data['orphaned_events']) . '',
                'content' => $orphanedEventsContent,
            ],
        ]);
    }

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

        $this->eventManager->reset();
    }
}


<?php

/**
 * Copyright (C) 2012 Igor Fedoronchuk
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category DIDWW
 * @package API2
 * @author Gonchar Igor <gigorok@gmail.com>
 * @copyright Copyright (C) 2012 by Igor Fedoronchuk
 * @license MIT
 */

namespace Didww\API2;

/**
 * CDRCollection
 * wraps collection of cdrs from API2 for per-page navigation
 * @category DIDWW
 * @package API2
 * @copyright Copyright (C) 2012 by Igor Fedoronchuk
 * @license MIT
 */
class SMSCollection extends ServerPaginatedCollection
{

    /**
     * filter by success argument
     * @var string
     */
    protected $success = null;

    /**
     * filter by destination
     * @var string
     */
    protected $destination = null;

    /**
     * filter by source
     * @var string
     */
    protected $source = null;

    /**
     * get success property
     * @return string
     */
    public function getSuccess()
    {
        return $this->success;
    }

    /**
     * set success property
     * @param string $success
     */
    public function setSuccess($success)
    {
        $this->success = (bool)$success;
    }

    /**
     *get $destination property
     * @return string
     */
    public function getDestination()
    {
        return $this->destination;
    }

    /**
     *get $destination property
     * @param string $destination
     */
    public function setDestination($destination)
    {
        $this->destination = $destination;
    }

    /**
     *get $source property
     * @return string
     */
    public function getSource()
    {
        return $this->source;
    }

    /**
     *get $source property
     * @param string $source
     */
    public function setSource($source)
    {
        $this->source = $source;
    }

    /**
     * get method for smslog
     * @return string
     */
    protected function getMethod()
    {
        return "getsmslog";
    }

    /**
     *get arguments for smslog method
     * @return array
     */
    protected function getArgs()
    {

        return array(
            'customer_id' => $this->getCustomerId(),
            'from_date' => $this->getFromDate(),
            'to_date' => $this->getToDate(),
            'destination' => $this->getDestination(),
            'source' => $this->getSource(),
            'success' => $this->getSuccess(),
            'limit' => $this->getLimit(),
            'offset' => $this->getOffset(),
            'order' => $this->getOrderBy(),
            'order_Dir' => $this->getOrderDir(),
        );
    }

    /**
     * get type of collection
     * @return string
     */
    protected function getCollectionType()
    {
        return 'Didww\API2\SMS';
    }

    /**
     *get key of response
     * @return string
     */
    protected function getResponseKey()
    {
        return "smslist";
    }

    /**
     * add element to list
     * @param stdObject $value
     */
    protected function appendToList($value)
    {
        $this->_list[] = new SMS((array)$value);
    }

}

/**
 * SMSCollectionException
 * Exception for class SMSCollection
 * @category DIDWW
 * @package API2
 * @copyright Copyright (C) 2012 by Igor Fedoronchuk
 * @license MIT
 */
class SMSCollectionException extends BaseException
{

}