<?php declare(strict_types=1);

namespace Swoft\Devtool\Model\Logic;

use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Bean\Annotation\Mapping\Inject;
use Swoft\Db\Types;
use Swoft\Devtool\FileGenerator;
use Swoft\Devtool\Model\Data\SchemaData;

/**
 * EntityLogic
 * @Bean()
 */
class EntityLogic
{
    /**
     * @var SchemaData
     * @Inject()
     */
    private $schemaData;

    /**
     * @param array $params
     */
    public function generate(array $params)
    {
        list($db, $inc, $exc, $path, $driver, $tablePrefix, $fieldPrefix, $tplFile, $tplDir) = $params;
        $tableSchemas = $this->schemaData->getSchemaTableData($driver, $db, $inc, $exc, $tablePrefix);
        foreach ($tableSchemas as $tableSchema) {
            $this->generateClass($driver, $db, $tableSchema, $fieldPrefix, $path, $tplFile, $tplDir);
        }
    }

    /**
     * @param string $driver
     * @param string $db
     * @param array  $tableSchema
     * @param string $fieldPrefix
     * @param string $path
     * @param string $tplFile
     * @param string $tplDir
     */
    private function generateClass(string $driver, string $db, array $tableSchema, string $fieldPrefix, string $path, string $tplFile, string $tplDir)
    {
        $mappingClass = $tableSchema['mapping'];
        $config       = [
            'tplFilename' => $tplFile,
            'tplDir'      => $tplDir,
            'className'   => $mappingClass,
        ];

        $file = alias($path);
        $file .= sprintf('/%s.php', $mappingClass);

        $columnSchemas = $this->schemaData->getSchemaColumnsData($driver, $db, $tableSchema['name'], $fieldPrefix);

        $genSetters    = [];
        $genGetters    = [];
        $genProperties = [];
        $useRequired   = false;
        foreach ($columnSchemas as $columnSchema) {
            list($propertyCode, $required) = $this->generateProperties($columnSchema, $tplDir);
            $genProperties[] = $propertyCode;
            if (!empty($required) && !$useRequired) {
                $useRequired = true;
            }

            $genSetters[] = $this->generateSetters($columnSchema, $tplDir);
            $genGetters[] = $this->generateGetters($columnSchema, $tplDir);
        }

        $setterStr   = implode("\n", $genSetters);
        $getterStr   = implode("\n", $genGetters);
        $propertyStr = implode("\n", $genProperties);
        $methodStr   = sprintf("%s\n\n%s", $setterStr, $getterStr);
        $usespace    = (!$useRequired) ? '' : 'use Swoft\Db\Bean\Annotation\Required;';

        $data = [
            'properties' => $propertyStr,
            'methods'    => $methodStr,
            'tableName'  => $tableSchema['name'],
            'entityName' => $mappingClass,
            'namespace'  => 'App\\Models\\Entity',
            'usespace'   => $usespace,
        ];

        $gen  = new FileGenerator($config);
        $gen->renderas($file, $data);
    }

    /**
     * @param array  $colSchema
     * @param string $tplDir
     *
     * @return array
     */
    private function generateProperties(array $colSchema, string $tplDir): array
    {
        $entityConfig = [
            'tplFilename' => 'property',
            'tplDir'      => $tplDir,
        ];

        // id
        $id = !empty($colSchema['key']) ? '* @Id()' : '';

        // required
        $isRequired = $colSchema['nullable'] === 'NO' && $colSchema['default'] === null;
        $required   = !empty($colSchema['key']) ? false : $isRequired;
        $required   = ($required == true) ? '* @Required()' : '';

        // default
        $default = $this->transferDefaultType($colSchema['type'], $colSchema['key'], $colSchema['default']);
        $default = ($default !== null) ? sprintf(', default=%s', $default) : '';

        $data         = [
            'type'         => $colSchema['phpType'],
            'propertyName' => $colSchema['mappingVar'],
            'column'       => $colSchema['name'],
            'columnType'   => $colSchema['mappingType'],
            'default'      => $default,
            'required'     => $required,
            'id'           => $id,
        ];
        $gen          = new FileGenerator($entityConfig);
        $propertyCode = $gen->render($data);

        return [$propertyCode, $required];
    }

    /**
     * @param array  $colSchema
     * @param string $tplDir
     *
     * @return string
     */
    private function generateGetters(array $colSchema, string $tplDir): string
    {
        $getterName = sprintf('get%s', ucfirst($colSchema['mappingName']));

        $config = [
            'tplFilename' => 'getter',
            'tplDir'      => $tplDir,
        ];
        $data   = [
            'returnType' => $colSchema['phpType'],
            'methodName' => sprintf('get%s', $getterName),
            'property'   => $colSchema['mappingName'],
        ];

        $gen = new FileGenerator($config);

        return $gen->render($data);
    }

    /**
     * @param array  $colSchema
     * @param string $tplDir
     *
     * @return string
     */
    private function generateSetters(array $colSchema, string $tplDir): string
    {
        $setterName = sprintf('set%s', ucfirst($colSchema['mappingName']));

        $config = [
            'tplFilename' => 'setter',
            'tplDir'      => $tplDir,
        ];

        $data = [
            'type'       => $colSchema['phpType'],
            'methodName' => $setterName,
            'paramName'  => $colSchema['mappingVar'],
            'property'   => $colSchema['mappingName'],
        ];

        $gen = new FileGenerator($config);

        return $gen->render($data);
    }

    /**
     * @param string $type
     * @param string $primaryKey
     * @param mixed  $default
     *
     * @return bool|float|int|null|string
     */
    private function transferDefaultType(string $type, string $primaryKey, $default)
    {
        if (!empty($primaryKey)) {
            return null;
        }

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

        $default = trim($default);
        switch ($type) {
            case Types::INT:
            case Types::NUMBER:
                $default = (int)$default;
                break;
            case Types::BOOL:
                $default = (bool)$default;
                break;
            case Types::FLOAT:
                $default = (float)$default;
                break;
            default:
                $default = sprintf('"%s"', $default);
                break;
        }

        return $default;
    }
}


<?php declare(strict_types=1);

namespace Swoft\Devtool\Model\Data;

use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Db\Driver\Driver;
use Swoft\Db\Driver\Mysql\Schema as MysqlSchema;
use Swoft\Db\Driver\Pgsql\Schema as PgsqlSchema;
use Swoft\Devtool\Model\Dao\MysqlSchemaDao;
use Swoft\Devtool\Model\Dao\SchemaInterface;
use Swoft\Stdlib\Helper\StringHelper;

/**
 * SchemaData
 * @Bean()
 */
class SchemaData
{
    /**
     * @param string $driver
     * @param string $db
     * @param string $inc
     * @param string $exc
     * @param string $tablePrefix
     *
     * @return array
     */
    public function getSchemaTableData(string $driver, string $db, string $inc, string $exc, string $tablePrefix): array
    {
        $schemaDao = $this->getSchemaDao($driver);
        $schemas   = $schemaDao->getTableSchema($db, $inc, $exc);
        foreach ($schemas as &$schema) {
            if (empty($tablePrefix)) {
                $mapping = $schema['name'];
            } else {
                $mapping = StringHelper::replaceFirst($tablePrefix, '', $schema['name']);
            }

            $schema['mapping'] = ucfirst(StringHelper::camel($mapping));
        }

        return $schemas;
    }

    /**
     * @param string $driver
     * @param string $db
     * @param string $table
     * @param string $fieldPrefix
     *
     * @return array
     */
    public function getSchemaColumnsData(string $driver, string $db, string $table, string $fieldPrefix): array
    {
        $schemaDao = $this->getSchemaDao($driver);
        list($mapingTypes, $phpTypes) = $this->getSchemaTypes($driver);
        $columnSchemas = $schemaDao->getColumnsSchema($db, $table);

        foreach ($columnSchemas as &$columnSchema) {
            $type = $columnSchema['type'];
            if (empty($fieldPrefix)) {
                $mappingName = $columnSchema['name'];
            } else {
                $mappingName = StringHelper::replaceFirst($fieldPrefix, '', $columnSchema['name']);
            }

            $columnSchema['mappingName'] = StringHelper::camel($mappingName);
            $columnSchema['mappingVar']  = sprintf('$%s', $columnSchema['mappingName']);
            $columnSchema['mappingType'] = $mapingTypes[$type]??'';
            $columnSchema['phpType']     = $phpTypes[$type]??'';
        }

        return $columnSchemas;
    }

    /**
     * @param string $driver
     *
     * @return \Swoft\Devtool\Model\Dao\SchemaInterface
     */
    private function getSchemaDao(string $driver): SchemaInterface
    {
        if ($driver == Driver::MYSQL) {
            return \bean(MysqlSchemaDao::class);
        }

        throw new \RuntimeException(sprintf('The %s driver does not support!', $driver));
    }

    /**
     * @param string $driver
     *
     * @return array
     */
    private function getSchemaTypes(string $driver): array
    {
        if ($driver == Driver::MYSQL) {
            return [MysqlSchema::$typeMap, MysqlSchema::$phpMap];
        }
        if ($driver == Driver::PGSQL) {
            return [PgsqlSchema::$typeMap, PgsqlSchema::$phpMap];
        }

        throw new \RuntimeException(sprintf('The %s schema does not support!', $driver));
    }
}


<?php declare(strict_types=1);
/**
 * This file is part of Swoft.
 *
 * @link    https://swoft.org
 * @document https://doc.swoft.org
 * @contact group@swoft.org
 * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE
 */

namespace Swoft\Devtool\Http\Middleware;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Co;
use Swoft\Config\Annotation\Mapping\Config;
use Swoft\Console\Console;
use Swoft\Devtool\DevTool;
use Swoft\Http\Message\Request;
use Swoft\Http\Server\Contract\MiddlewareInterface;

/**
 * Class DevToolMiddleware - Custom middleware
 * @Bean()
 */
class DevToolMiddleware implements MiddlewareInterface
{
    /**
     * @Config("devtool.logHttpRequestToConsole")
     * @var bool
     */
    public $logHttpRequestToConsole = false;

    /**
     * @param \Psr\Http\Message\ServerRequestInterface|Request $request
     * @param \Psr\Http\Server\RequestHandlerInterface         $handler
     *
     * @return \Psr\Http\Message\ResponseInterface
     * @throws \Throwable
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        // Before request
        $path = $request->getUri()->getPath();

        if ($this->logHttpRequestToConsole) {
            Console::log(\sprintf('%s %s', $request->getMethod(), $path), [], 'debug', [
                'HttpServer',
                // 'WorkerId' => App::getWorkerId(),
                'CoId' => Co::tid()
            ]);
        }

        // If it is not an ajax request, then render vue index file.
        if (0 === \strpos($path, DevTool::ROUTE_PREFIX) && !$request->isAjax()) {
            $json = $request->query('json');

            if (null === $json) {
                return \view(\alias('@devtool/web/dist/index.html'));
            }
        }

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

        // After request
        return $response->withAddedHeader('Swoft-DevTool-Version', '1.0.0');
    }
}


<?php declare(strict_types=1);

namespace Swoft\Devtool\Http\Controller;

use Swoft\Helper\ProcessHelper;
use Swoft\Http\Message\Request;
use Swoft\Http\Server\Annotation\Mapping\Controller;
use Swoft\Http\Server\Annotation\Mapping\RequestMapping;
use Swoft\Http\Server\Annotation\Mapping\RequestMethod;
use Swoft\Stdlib\Helper\Sys;

/**
 * Class ServerController
 *
 * @Controller(prefix="/__devtool/server/")
 */
class ServerController
{
    /**
     * Get server config
     * @RequestMapping(route="config", method=RequestMethod::GET)
     * @return array
     */
    public function config(): array
    {
        return [
            'swoole' => \server()->getSetting(),
            'server' => \server()->getTypeName(),
        ];
    }

    /**
     * get all registered events list
     * @RequestMapping(route="events", method=RequestMethod::GET)
     * @param Request $request
     * @return array
     */
    public function events(Request $request): array
    {
        // 1 server event
        // 2 swoole event
        $type = (int)$request->query('type');

        if ($type === 1) {
            return ServerListenerCollector::getCollector();
        }

        if ($type === 2) {
            return SwooleListenerCollector::getCollector();
        }

        return [
            'server' => ServerListenerCollector::getCollector(),
            'swoole' => SwooleListenerCollector::getCollector(),
        ];
    }

    /**
     * Get php extensions list
     *
     * @RequestMapping(route="php-exts", method=RequestMethod::GET)
     * @return array
     */
    public function phpExt(): array
    {
        return \get_loaded_extensions();
    }

    /**
     * Get swoole server stats
     * @RequestMapping(route="stats", method=RequestMethod::GET)
     * @return array
     */
    public function stats(): array
    {
        if (!\server()) {
            return ['msg' => 'server is not running'];
        }

        $stat = \server()->getSwooleStats();
        // start date
        $stat['start_date'] = \date('Y-m-d H:i:s', $stat['start_time']);

        return $stat;
    }

    /**
     * get crontab list
     * @RequestMapping(route="crontab", method=RequestMethod::GET)
     * @return array
     * @throws \Throwable
     */
    public function cronTab(): array
    {
        if (!\Swoft::hasBean('crontab')) {
            return [];
        }

        /** @var \Swoft\Task\Crontab\Crontab $cronTab */
        $cronTab = \bean('crontab');

        return $cronTab->getTasks();
    }

    /**
     * get swoole info
     * @RequestMapping(route="processes", method=RequestMethod::GET)
     * @return array
     * @throws \InvalidArgumentException
     * @throws \RuntimeException
     */
    public function process(): array
    {
        [$code, $return, $error] = Sys::run('ps aux | grep swoft');
        if ($code) {
            return ['code' => 404, 'msg' => $error];
        }

        return [
            'raw' => $return
        ];
    }

    /**
     * Get swoole info
     * @RequestMapping(route="swoole-info", method=RequestMethod::GET)
     * @return array
     * @throws \InvalidArgumentException
     * @throws \RuntimeException
     */
    public function swoole(): array
    {
        [$code, $return, $error] = Sys::run('php --ri swoole');
        if ($code) {
            return ['code' => 404, 'msg' => $error];
        }

        // format
        $str = \str_replace("\r\n", "\n", \trim($return));
        [, $enableStr, $directiveStr] = \explode("\n\n", $str);

        $directive = $this->formatSwooleInfo($directiveStr);
        \array_shift($directive);

        return [
            'raw'       => $return,
            'enable'    => $this->formatSwooleInfo($enableStr),
            'directive' => $directive,
        ];
    }

    /**
     * @param string $str
     * @return array
     */
    private function formatSwooleInfo(string $str): array
    {
        $data  = [];
        $lines = \explode("\n", \trim($str));

        foreach ($lines as $line) {
            [$name, $value] = \explode(' => ', $line);
            $data[] = [
                'name'  => $name,
                'value' => $value,
            ];
        }

        return $data;
    }
}


<?php declare(strict_types=1);

namespace Swoft\Devtool\Http\Controller;

use Swoft\Aop\Aop;
use Swoft\Bean\BeanFactory;
use Swoft\Config\Config;
use Swoft\Devtool\Helper\DevToolHelper;
use Swoft\Http\Message\Request;
use Swoft\Http\Server\Annotation\Mapping\Controller;
use Swoft\Http\Server\Annotation\Mapping\RequestMapping;
use Swoft\Http\Server\Annotation\Mapping\RequestMethod;

/**
 * Class AppController
 *
 * @Controller(prefix="/__devtool/app")
 */
class AppController
{
    /**
     * get app info
     * @RequestMapping(route="env", method=RequestMethod::GET)
     * @return array
     */
    public function index(): array
    {
        return [
            'os'            => \PHP_OS,
            'phpVersion'    => \PHP_VERSION,
            'swooleVersion' => \SWOOLE_VERSION,
            'swoftVersion'  => \Swoft::VERSION,
            'appName'       => \APP_NAME,
            'basePath'      => \BASE_PATH,
        ];
    }

    /**
     * Get app config
     * @RequestMapping(route="config", method=RequestMethod::GET)
     * @param Request $request
     * @return array|mixed
     * @throws \Throwable
     */
    public function config(Request $request)
    {
        if ($key = $request->query('key')) {
            /** @see Config::get() */
            return \config($key);
        }

        /** @see Config::toArray() */
        return \bean('config')->toArray();
    }

    /**
     * get app pools
     * @RequestMapping(route="pools", method=RequestMethod::GET)
     * @param Request $request
     * @return array
     */
    public function pools(Request $request): array
    {
        if ($name = $request->query('name')) {
            if (!App::hasPool($name)) {
                return [];
            }

            /** @var PoolConfigInterface $poolConfig */
            $poolConfig = App::getPool($name)->getPoolConfig();

            return $poolConfig->toArray();
        }

        return PoolCollector::getCollector();
    }

    /**
     * get app beans
     * @RequestMapping(route="beans", method=RequestMethod::GET)
     * @param Request $request
     * @return array
     */
    public function beans(Request $request): array
    {
        if ($name = $request->query('name')) {
            return [];
        }

        return BeanFactory::getContainer()->getNames();
    }

    /**
     * get app beans config
     * @RequestMapping(route="beans-config", method=RequestMethod::GET)
     * @param Request $request
     * @return array
     */
    public function beansConfig(Request $request): array
    {
        if ($name = $request->query('name')) {
            return [];
        }

        return [];
    }

    /**
     * get app path aliases
     * @RequestMapping(route="aliases", method=RequestMethod::GET)
     * @return array
     */
    public function pathAliases(): array
    {
        return \Swoft::getAliases();
    }

    /**
     * Get all registered application events list
     * @RequestMapping(route="events", method=RequestMethod::GET)
     * @param Request $request
     * @return array
     * @throws \Throwable
     */
    public function events(Request $request): array
    {
        /** @var \Swoft\Event\Manager\EventManager $em */
        $em = \bean('eventManager');

        if ($event = \trim($request->query('name'))) {
            if (!$queue = $em->getListenerQueue($event)) {
                return ['msg' => 'event name is invalid: ' . $event];
            }

            $classes = [];
            foreach ($queue->getIterator() as $listener) {
                $classes[] = \get_class($listener);
            }

            return $classes;
        }

        return $em->getListenedEvents();
    }

    /**
     * Get all registered components
     *
     * @RequestMapping(route="components", method=RequestMethod::GET)
     * @return array
     * @throws \InvalidArgumentException
     */
    public function components(): array
    {
        $lockFile = \Swoft::getAlias('@base/composer.lock');

        return DevToolHelper::parseComposerLockFile($lockFile);
    }

    /**
     * Get all registered aop handlers
     *
     * @RequestMapping(route="aop/handlers", method=RequestMethod::GET)
     * @return array
     * @throws \Throwable
     */
    public function aopHandles(): array
    {
        /** @var Aop $aop */
        $aop = \bean(Aop::class);

        return $aop->getAspects();
    }

    /**
     * Get all registered http middleware list
     *
     * @RequestMapping(route="http/middles", method=RequestMethod::GET)
     * @param Request $request
     * @return array
     * @throws \Throwable
     */
    public function httpMiddles(Request $request): array
    {
        /** @var \Swoft\Http\Server\HttpDispatcher $dispatcher */
        $dispatcher = \bean('serverDispatcher');
        $middleType = (int)$request->query('type');

        // 1: only return user's
        if ($middleType === 1) {
            return $dispatcher->getMiddlewares();
        }

        return $dispatcher->requestMiddleware();
    }

    /**
     * get all registered rpc middleware list
     * @RequestMapping(route="rpc/middles", method=RequestMethod::GET)
     * @param Request $request
     * @return array
     * @throws \Throwable
     */
    public function rpcMiddles(Request $request): array
    {
        $beanName = 'serviceDispatcher';
        if (!\Swoft::hasBean($beanName)) {
            return [];
        }

        /** @var \Swoft\Rpc\Server\ServiceDispatcher $dispatcher */
        $dispatcher = \bean($beanName);
        $middleType = (int)$request->query('type');

        // 1 only return user's
        if ($middleType === 1) {
            return $dispatcher->getMiddlewares();
        }

        return $dispatcher->requestMiddleware();
    }
}


<?php declare(strict_types=1);

namespace Swoft\Devtool\Model\Dao;

use Swoft\Bean\Annotation\Mapping\Bean;

/**
 * Schema
 * @Bean()
 */
class MysqlSchemaDao implements SchemaInterface
{
    /**
     * @param string $db
     * @param string $inc
     * @param string $exc
     *
     * @return array
     */
    public function getTableSchema(string $db, string $inc, string $exc): array
    {
        $query = Query::table('information_schema.`tables`')->where('TABLE_SCHEMA', $db)->where('TABLE_TYPE', 'BASE TABLE');
        if (!empty($inc)) {
            $query = $query->whereIn('TABLE_NAME', explode(',', $inc));
        }

        if (!empty($exc)) {
            $query = $query->whereNotIn('TABLE_NAME', explode(',', $exc));
        }

        $columns = [
            'TABLE_NAME'    => 'name',
            'TABLE_COMMENT' => 'comment',
        ];

        return $query->get($columns)->getResult();
    }

    /**
     * @param string $db
     * @param string $table
     *
     * @return array
     */
    public function getColumnsSchema(string $db, string $table): array
    {
        $query   = Query::table('information_schema.`columns`')->where('TABLE_SCHEMA', $db)->where('TABLE_NAME', $table);
        $columns = [
            'COLUMN_NAME'              => 'name',
            'DATA_TYPE'                => 'type',
            'COLUMN_DEFAULT'           => 'default',
            'COLUMN_KEY'               => 'key',
            'IS_NULLABLE'              => 'nullable',
            'COLUMN_TYPE'              => 'columnType',
            'COLUMN_COMMENT'           => 'columnComment',
            'CHARACTER_MAXIMUM_LENGTH' => 'length',
        ];

        return $query->get($columns)->getResult();
    }
}


<?php declare(strict_types=1);

namespace Swoft\Devtool\Command;

use Swoft\Bean\BeanFactory;
use Swoft\Console\Annotation\Mapping\Command;
use Swoft\Console\Annotation\Mapping\CommandMapping;
use Swoft\Console\Annotation\Mapping\CommandOption;
use Swoft\Console\Helper\Show;
use Swoft\Console\Output\Output;
use Swoft\Stdlib\Helper\DirHelper;
use Swoft\Stdlib\Helper\Sys;

/**
 * There are some help command for application[by <cyan>devtool</cyan>]
 *
 * @Command(coroutine=false)
 */
class AppCommand
{
    /**
     * init the project, will create runtime dirs
     *
     * @CommandMapping("init", usage="{fullCommand} [arguments] [options]")
     * @throws \InvalidArgumentException
     * @throws \RuntimeException
     */
    public function initApp(): void
    {
        $tmpDir = \Swoft::getAlias('@runtime');
        $names  = [
            'logs',
            'uploadfiles'
        ];

        \output()->writeln('Create runtime directories: ' . \implode(',', $names));

        foreach ($names as $name) {
            DirHelper::make($tmpDir . '/' . $name);
        }

        \output()->writeln('<success>OK</success>');
    }

    /**
     * Print current system environment information
     *
     * @CommandMapping()
     *
     * @param Output $output
     *
     * @throws \RuntimeException
     * @throws \Swoft\Bean\Exception\ContainerException
     */
    public function env(Output $output): void
    {
        $info = [
            // "<bold>System environment info</bold>\n",
            'OS'             => \PHP_OS,
            'Php version'    => \PHP_VERSION,
            'Swoole version' => \SWOOLE_VERSION,
            'Swoft version'  => \Swoft::VERSION,
            'App Name'       => \config('name', 'unknown'),
            'Base Path'      => \BASE_PATH,
        ];

        Show::aList($info, 'System Environment Info');
    }

    /**
     * @CommandMapping()
     * @CommandOption("type", desc="Display the bean names of the type")
     */
    public function bean(): void
    {
        $names = BeanFactory::getContainer()->getNames();
        $type = \input()->getOpt('type');

        if (isset($names[$type])) {
            Show::prettyJSON($names[$type]);

            return;
        }

        Show::prettyJSON($names);
    }

    /**
     * Check current operating environment information
     *
     * @CommandMapping()
     * @param Output $output
     *
     * @throws \RuntimeException
     */
    public function check(Output $output): void
    {
        // Env check
        [$code, $return,] = Sys::run('php --ri swoole');
        $asyncRdsEnabled = $code === 0 ? \strpos($return, 'redis client => enabled') : false;

        $list = [
            "<bold>Runtime environment check</bold>\n",
            'PHP version is greater than 7.1?'    => self::wrap(\PHP_VERSION_ID > 70100, 'current is ' .
                \PHP_VERSION),
            'Swoole extension is installed?'      => self::wrap(\extension_loaded('swoole')),
            'Swoole version is greater than 4.3?' => self::wrap(\version_compare(\SWOOLE_VERSION, '4.3.0', '>='),
                'current is ' . \SWOOLE_VERSION),
            'Swoole async redis is enabled?'      => self::wrap($asyncRdsEnabled),
            'Swoole coroutine is enabled?'        => self::wrap(\class_exists('Swoole\Coroutine', false)),
            "\n<bold>Extensions that conflict with 'swoole'</bold>\n",
            // ' extensions'                             => 'installed',
            ' - zend'                             => self::wrap(!\extension_loaded('zend'),
                'Please disabled it, otherwise swoole will be affected!', true),
            ' - xdebug'                           => self::wrap(!\extension_loaded('xdebug'),
                'Please disabled it, otherwise swoole will be affected!', true),
            ' - xhprof'                           => self::wrap(!\extension_loaded('xhprof'),
                'Please disabled it, otherwise swoole will be affected!', true),
            ' - blackfire'                        => self::wrap(!\extension_loaded('blackfire'),
                'Please disabled it, otherwise swoole will be affected!', true),
        ];

        $buffer = [];
        $pass   = $total = 0;

        foreach ($list as $question => $value) {
            if (\is_int($question)) {
                $buffer[] = $value;
                continue;
            }

            $total++;

            if ($value[0]) {
                $pass++;
            }

            $question = \str_pad($question, 45);
            $buffer[] = \sprintf('  <comment>%s</comment> %s', $question, $value[1]);
        }

        $buffer[] = "\nCheck total: <bold>$total</bold>, Pass the check: <success>$pass</success>";

        $output->writeln($buffer);
    }

    /**
     * @param             $condition
     * @param string|null $msg
     * @param bool        $showOnFalse
     *
     * @return array
     */
    public static function wrap($condition, string $msg = null, $showOnFalse = false): array
    {
        $result = $condition ? '<success>Yes</success>' : '<red>No</red>';
        $des    = '';

        if ($msg) {
            if ($showOnFalse) {
                $des = !$condition ? " ($msg)" : '';
            } else {
                $des = " ($msg)";
            }
        }

        return [(bool)$condition, $result . $des];
    }
}


<?php

declare(strict_types=1);

/*
 * This file is part of Laravel Binput.
 *
 * (c) Graham Campbell <graham@alt-three.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace GrahamCampbell\Binput;

use GrahamCampbell\SecurityCore\Security;
use Illuminate\Http\Request;

/**
 * This is the binput class.
 *
 * @author Graham Campbell <graham@alt-three.com>
 */
class Binput
{
    /**
     * The request instance.
     *
     * @var \Illuminate\Http\Request
     */
    protected $request;

    /**
     * The security instance.
     *
     * @var \GrahamCampbell\SecurityCore\Security
     */
    protected $security;

    /**
     * Create a new instance.
     *
     * @param \Illuminate\Http\Request              $request
     * @param \GrahamCampbell\SecurityCore\Security $security
     *
     * @return void
     */
    public function __construct(Request $request, Security $security)
    {
        $this->request = $request;
        $this->security = $security;
    }

    /**
     * Get all of the input and files for the request.
     *
     * @param bool $trim
     * @param bool $clean
     *
     * @return array
     */
    public function all(bool $trim = true, bool $clean = true)
    {
        $values = $this->request->all();

        return $this->clean($values, $trim, $clean);
    }

    /**
     * Get an input item from the request.
     *
     * @param string $key
     * @param mixed  $default
     * @param bool   $trim
     * @param bool   $clean
     *
     * @return mixed
     */
    public function get(string $key, $default = null, bool $trim = true, bool $clean = true)
    {
        $value = $this->request->input($key, $default);

        return $this->clean($value, $trim, $clean);
    }

    /**
     * Get an input item from the request.
     *
     * This is an alias to the get method.
     *
     * @param string $key
     * @param mixed  $default
     * @param bool   $trim
     * @param bool   $clean
     *
     * @return mixed
     */
    public function input(string $key, $default = null, bool $trim = true, bool $clean = true)
    {
        return $this->get($key, $default, $trim, $clean);
    }

    /**
     * Get a subset of the items from the input data.
     *
     * @param string|string[] $keys
     * @param bool            $trim
     * @param bool            $clean
     *
     * @return array
     */
    public function only($keys, bool $trim = true, bool $clean = true)
    {
        $values = [];
        foreach ((array) $keys as $key) {
            $values[$key] = $this->get($key, null, $trim, $clean);
        }

        return $values;
    }

    /**
     * Get all of the input except for a specified array of items.
     *
     * @param string|string[] $keys
     * @param bool            $trim
     * @param bool            $clean
     *
     * @return array
     */
    public function except($keys, bool $trim = true, bool $clean = true)
    {
        $values = $this->request->except((array) $keys);

        return $this->clean($values, $trim, $clean);
    }

    /**
     * Get a mapped subset of the items from the input data.
     *
     * @param string[] $keys
     * @param bool     $trim
     * @param bool     $clean
     *
     * @return array
     */
    public function map(array $keys, bool $trim = true, bool $clean = true)
    {
        $values = $this->only(array_keys($keys), $trim, $clean);

        $new = [];
        foreach ($keys as $key => $value) {
            $new[$value] = array_get($values, $key);
        }

        return $new;
    }

    /**
     * Get an old input item from the request.
     *
     * @param string $key
     * @param mixed  $default
     * @param bool   $trim
     * @param bool   $clean
     *
     * @return mixed
     */
    public function old(string $key, $default = null, bool $trim = true, bool $clean = true)
    {
        $value = $this->request->old($key, $default);

        return $this->clean($value, $trim, $clean);
    }

    /**
     * Clean a specified value or values.
     *
     * @param mixed $value
     * @param bool  $trim
     * @param bool  $clean
     *
     * @return mixed
     */
    public function clean($value, bool $trim = true, bool $clean = true)
    {
        if (is_bool($value) || is_int($value) || is_float($value)) {
            return $value;
        }

        $final = null;

        if ($value !== null) {
            if (is_array($value)) {
                $all = $value;
                $final = [];
                foreach ($all as $key => $value) {
                    if ($value !== null) {
                        $final[$key] = $this->clean($value, $trim, $clean);
                    }
                }
            } else {
                if ($value !== null) {
                    $final = $this->process((string) $value, $trim, $clean);
                }
            }
        }

        return $final;
    }

    /**
     * Process a specified value.
     *
     * @param string $value
     * @param bool   $trim
     * @param bool   $clean
     *
     * @return string
     */
    protected function process(string $value, bool $trim = true, bool $clean = true)
    {
        if ($trim) {
            $value = trim($value);
        }

        if ($clean) {
            $value = $this->security->clean($value);
        }

        return $value;
    }

    /**
     * Return the request instance.
     *
     * @return \Illuminate\Http\Request
     */
    public function getRequest()
    {
        return $this->request;
    }

    /**
     * Set the request instance.
     *
     * @param \Illuminate\Http\Request $request
     *
     * @return void
     */
    public function setRequest(Request $request)
    {
        $this->request = $request;
    }

    /**
     * Return the security instance.
     *
     * @return \GrahamCampbell\SecurityCore\Security
     */
    public function getSecurity()
    {
        return $this->security;
    }

    /**
     * Dynamically call all other methods on the request object.
     *
     * @param string $method
     * @param array  $parameters
     *
     * @return mixed
     */
    public function __call(string $method, array $parameters)
    {
        return $this->request->$method(...$parameters);
    }
}


<?php

declare(strict_types=1);

/*
 * This file is part of Laravel Binput.
 *
 * (c) Graham Campbell <graham@alt-three.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace GrahamCampbell\Binput;

use Illuminate\Contracts\Container\Container;
use Illuminate\Support\ServiceProvider;

/**
 * This is the binput service provider class.
 *
 * @author Graham Campbell <graham@alt-three.com>
 */
class BinputServiceProvider extends ServiceProvider
{
    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->registerBinput();
    }

    /**
     * Register the binput class.
     *
     * @return void
     */
    protected function registerBinput()
    {
        $this->app->singleton('binput', function (Container $app) {
            $request = $app['request'];
            $security = $app['security'];

            $binput = new Binput($request, $security);
            $app->refresh('request', $binput, 'setRequest');

            return $binput;
        });

        $this->app->alias('binput', Binput::class);
    }

    /**
     * Get the services provided by the provider.
     *
     * @return string[]
     */
    public function provides()
    {
        return [
            'binput',
        ];
    }
}


<?php
/******************************************************************************
 * Author: Petr Suchy (xsuchy09) <suchy@wamos.cz> <http://www.wamos.cz>
 * Subject: WAMOS <http://www.wamos.cz>
 * Project: utmcookie
 * Copyright: (c) Petr Suchy (xsuchy09) <suchy@wamos.cz> <http://www.wamos.cz>
 *****************************************************************************/

namespace UtmCookie;

use DateInterval;
use DateTime;
use stdClass;
use UnexpectedValueException;

/**
 * Utm-Cookie saves utm parameters from url into cookie with defined lifetime (default 7 days).
 * Than cookie (utm) can be used later without parsing google or any other cookies.
 *
 * @package UtmCookie
 * @version 1.0.1
 * @author Petr Suchy (xsuchy09) <suchy@wamos.cz> <http://www.wamos.cz>
 * @license Apache License 2.0
 * @link https://github.com/xsuchy09/utm-cookie
 */
class UtmCookie
{

	const DEFAULT_UTM_COOKIE_LIFETIME = 'P7D';

	/**
	 * Name of cookie where will be saved utm params.
	 *
	 * @var string
	 */
	private static $utmCookieName;

	/**
	 * @var array
	 */
	private static $utmCookie = null;

	/**
	 * Lifetime of utmCookie
	 *
	 * @var DateInterval
	 */
	private static $lifetime;

	/**
	 * If overwrite all utm values when even one is set in get. Default true.
	 *
	 * @var bool
	 */
	private static $overwrite;

	/**
	 * Path for cookie. Default "/" so not empty like in setcookie PHP function!
	 *
	 * @var string
	 */
	private static $path = '/';

	/**
	 * Domain for cookie.
	 *
	 * @var string
	 */
	private static $domain = '';

	/**
	 * If cookie should be secured (same as $secure parameter in setcookie PHP function).
	 *
	 * @var bool
	 */
	private static $secure = false;

	/**
	 * If cookie should be http only (same as $httponly parameter in setcookie PHP function).
	 *
	 * @var bool
	 */
	private static $httpOnly = false;

	/**
	 * Constructor - private.
	 */
	private function __construct()
	{

	}

	/**
	 * Initialize. Get values from _GET and _COOKIES and save to UtmCookie. Init self::$utmCookie value.
	 *
	 * @return void
	 */
	public static function init()
	{
		// if initializated, just return
		if (self::$utmCookie !== null) {
			return;
		}

		self::initStaticValues();

		// utm from _COOKIE
		$utmCookieFilter = filter_input(INPUT_COOKIE, self::$utmCookieName, FILTER_SANITIZE_STRING, FILTER_REQUIRE_ARRAY);
		if (false === is_array($utmCookieFilter)) {
			$utmCookieFilter = [];
		}
		$utmCookie = self::removeNullValues($utmCookieFilter);

		// utm from _GET
		$utmGetFilter = filter_input_array(
				INPUT_GET,
				[
					'utm_campaign' => FILTER_SANITIZE_STRING,
					'utm_medium'   => FILTER_SANITIZE_STRING,
					'utm_source'   => FILTER_SANITIZE_STRING,
					'utm_term'     => FILTER_SANITIZE_STRING,
					'utm_content'  => FILTER_SANITIZE_STRING
				]
		);
		if (false === is_array($utmGetFilter)) {
			$utmGetFilter = [];
		}
		$utmGet = self::removeNullValues($utmGetFilter);

		if (count($utmGet) !== 0 && self::$overwrite === true) {
			$utmCookieSave = array_merge(self::$utmCookie, $utmGet);
		} else {
			$utmCookieSave = array_merge(self::$utmCookie, $utmCookie, $utmGet);
		}

		if (count($utmGet) !== 0) {
			self::save($utmCookieSave);
		} else {
			self::$utmCookie = $utmCookieSave;
		}
	}

	/**
	 * Initialize static values to default (or empty) values.
	 */
	private static function initStaticValues()
	{
		if (self::$utmCookieName === null) {
			self::$utmCookieName = 'utm';
		}

		self::$utmCookie = [
			'utm_campaign' => null,
			'utm_medium'   => null,
			'utm_source'   => null,
			'utm_term'     => null,
			'utm_content'  => null
		];

		if (self::$lifetime === null) {
			self::$lifetime = new DateInterval(self::DEFAULT_UTM_COOKIE_LIFETIME);
		}

		if (self::$overwrite === null) {
			self::$overwrite = true;
		}
	}

	/**
	 * Remove elements with null values from array.
	 *
	 * @param array|null $array
	 *
	 * @return array
	 */
	private static function removeNullValues(array $array = null)
	{
		// null (undefined) or false (filter failed)
		if ($array === null || $array === false) {
			return [];
		}
		return array_filter(
				$array,
				function($value) {
					return $value !== null;
				}
		);
	}

	/**
	 * Set name of cookie where will be saved utm params.
	 *
	 * @param string $utmCookieName
	 */
	public static function setName(string $utmCookieName)
	{
		self::$utmCookieName = $utmCookieName;
		// cancel previous init
		self::$utmCookie = null;
	}

	/**
	 * Set lifetime of utm cookie.
	 *
	 * @param DateInterval $lifetime
	 */
	public static function setLifetime(DateInterval $lifetime)
	{
		self::$lifetime = $lifetime;
	}

	/**
	 * Set if even one utm value in _GET will overwrite all utm values or not.
	 *
	 * @param bool $overwrite
	 */
	public static function setOverwrite(bool $overwrite)
	{
		self::$overwrite = $overwrite;
		// cancel previous init
		self::$utmCookie = null;
	}

	/**
	 * Set path for cookie.
	 *
	 * @param string $path
	 */
	public static function setPath(string $path)
	{
		self::$path = $path;
	}

	/**
	 * Set domain for cookie.
	 *
	 * @param string $domain
	 */
	public static function setDomain(string $domain)
	{
		self::$domain = $domain;
	}

	/**
	 * Set secure for cookie.
	 *
	 * @param type $secure
	 */
	public static function setSecure(bool $secure)
	{
		self::$secure = $secure;
	}

	/**
	 * Set httponly for cookie.
	 *
	 * @param type $httpOnly
	 */
	public static function setHttpOnly(bool $httpOnly)
	{
		self::$httpOnly = $httpOnly;
	}

	/**
	 * Get all utm values or just value of utm with specific key.
	 *
	 * @param string|null $key Default null (return all values as array).
	 *
	 * @return string|array|null Return string value, array or null if not set.
	 */
	public static function get(?string $key = null)
	{
		self::init();

		if ($key === null) {
			return self::$utmCookie;
		} else {
			if (mb_strpos($key, 'utm_') !== 0) {
				$key = 'utm_' . $key;
			}
			if (false === array_key_exists($key, self::$utmCookie)) {
				throw new UnexpectedValueException(sprintf('Argument $key has unexpecte value "%s". Utm value with key "%s" does not exists.', $key, $key));
			} else {
				return self::$utmCookie[$key];
			}
		}
	}

	/**
	 * Return all utm values as stdClass.
	 *
	 * @return stdClass
	 */
	public static function getObject(): stdClass
	{
		return (object)self::get();
	}

	/**
	 * Save utmCookie value into _COOKIE and set actual self::$utmCookie value (call only from init).
	 *
	 * @param array $utmCookieSave
	 */
	private static function save(array $utmCookieSave)
	{
		$expire = new DateTime();
		$expire->add(self::$lifetime);

		foreach ($utmCookieSave as $key => $value) {
			setcookie(self::$utmCookieName . '[' . $key . ']', $value, $expire->getTimestamp(), self::$path, self::$domain, self::$secure, self::$httpOnly);
		}

		self::$utmCookie = $utmCookieSave;
	}
}


<?php

/**
 * This file is part of MetaModels/core.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/core
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     Sven Baumann <baumann.sv@gmail.com>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\CoreBundle\EventListener\DcGeneral\EnvironmentPopulator;

use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface;
use MetaModels\DcGeneral\Events\MetaModel\PopulateAttributeEvent;
use MetaModels\IFactory;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * This class handles the MetaModels attribute populating.
 */
class AttributePopulator
{
    use MetaModelPopulatorTrait;

    /**
     * The event dispatcher.
     *
     * @var EventDispatcherInterface
     */
    private $dispatcher;

    /**
     * The MetaModels factory.
     *
     * @var IFactory
     */
    private $factory;

    /**
     * Create a new instance.
     *
     * @param EventDispatcherInterface $dispatcher The event dispatcher.
     * @param IFactory                 $factory    The MetaModels factory.
     */
    public function __construct(EventDispatcherInterface $dispatcher, IFactory $factory)
    {
        $this->dispatcher = $dispatcher;
        $this->factory    = $factory;
    }

    /**
     * Populate the environment.
     *
     * @param EnvironmentInterface $environment The environment.
     *
     * @return void
     */
    public function populate(EnvironmentInterface $environment)
    {
        $metaModel = $this->factory->getMetaModel($environment->getDataDefinition()->getName());
        foreach ($metaModel->getAttributes() as $attribute) {
            $event = new PopulateAttributeEvent($metaModel, $attribute, $environment);
            // Trigger BuildAttribute Event.
            $this->dispatcher->dispatch($event::NAME, $event);
        }
    }
}


<?php

/**
 * This file is part of MetaModels/core.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/core
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     Sven Baumann <baumann.sv@gmail.com>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\Dca;

use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent;

/**
 * This provides the backend section options.
 */
class BackendSectionOptionListener
{
    /**
     * Retrieve a list of all backend sections, like "content", "system" etc.
     *
     * @param GetPropertyOptionsEvent $event The event.
     *
     * @return void
     *
     * @SuppressWarnings(PHPMD.Superglobals)
     * @SuppressWarnings(PHPMD.CamelCaseVariableName)
     */
    public function handle(GetPropertyOptionsEvent $event)
    {
        if (('tl_metamodel_dca' !== $event->getEnvironment()->getDataDefinition()->getName())
            || ('backendsection' !== $event->getPropertyName())) {
            return;
        }

        $event->setOptions(array_keys($GLOBALS['BE_MOD']));
    }
}


<?php

/**
 * This file is part of MetaModels/core.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/core
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     David Maack <david.maack@arcor.de>
 * @author     Richard Henkenjohann <richardhenkenjohann@googlemail.com>
 * @author     Stefan Heimes <stefan_heimes@hotmail.com>
 * @author     Sven Baumann <baumann.sv@gmail.com>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\Render;

use MetaModels\Helper\ContaoController;

/**
 * Template class for MetaModels.
 * In most aspects this behaves identically to the FrontendTemplate class from Contao but it differs in respect to
 * format selection.
 * The format is being determined upon parsing and not upon instantiation. There is also an optional "fail on not
 * found" flag, which defaults to false and therefore one can parse the template and have zero output instead of
 * cluttering the frontend with exceptions.
 *
 * @SuppressWarnings(PHPMD.TooManyPublicMethods)
 */
class Template
{
    /**
     * Template file.
     *
     * @var string
     */
    protected $strTemplate;

    /**
     * Parent template.
     *
     * @var string
     */
    protected $strParent;

    /**
     * Default template.
     *
     * @var string
     */
    protected $strDefault;

    /**
     * Template data.
     *
     * @var array
     */
    protected $arrData = array();

    /**
     * Current output format. Only valid when within {@link MetaModelTemplate::parse()}.
     *
     * @var string
     */
    protected $strFormat = null;

    /**
     * Blocks.
     *
     * @var array
     */
    protected $arrBlocks = array();

    /**
     * Block names.
     *
     * @var array
     */
    protected $arrBlockNames = array();

    /**
     * Makes all protected methods from class Controller callable publically.
     *
     * @param string $strMethod The method name.
     *
     * @param array  $arrArgs   The parameters for the method.
     *
     * @return mixed
     */
    public function __call($strMethod, $arrArgs)
    {
        return call_user_func_array(array(ContaoController::getInstance(), $strMethod), $arrArgs);
    }

    /**
     * Create a new template instance.
     *
     * @param string $strTemplate The name of the template file.
     */
    public function __construct($strTemplate = '')
    {
        $this->strTemplate = $strTemplate;
    }

    /**
     * Set an object property.
     *
     * @param string $strKey   The name of the property.
     *
     * @param mixed  $varValue The value to set.
     *
     * @return void
     */
    public function __set($strKey, $varValue)
    {
        $this->arrData[$strKey] = $varValue;
    }

    /**
     * Return an object property.
     *
     * @param string $strKey The name of the property.
     *
     * @return mixed
     *
     * @SuppressWarnings(PHPMD.Superglobals)
     * @SuppressWarnings(PHPMD.CamelCaseVariableName)
     */
    public function __get($strKey)
    {
        if (array_key_exists($strKey, $this->arrData)) {
            return $this->arrData[$strKey];
        }

        if (!empty($GLOBALS['TL_CONFIG']['debugMode'])) {
            trigger_error($this->getName() . ': Undefined template variable: ' . $strKey, E_USER_WARNING);
        }
        return null;
    }

    /**
     * Check whether a property is set.
     *
     * @param string $strKey The name of the property.
     *
     * @return boolean
     */
    public function __isset($strKey)
    {
        return isset($this->arrData[$strKey]);
    }

    /**
     * Set the template data from an array.
     *
     * @param array $arrData The properties to be set.
     *
     * @return void
     */
    public function setData($arrData)
    {
        $this->arrData = $arrData;
    }

    /**
     * Return the template data as array.
     *
     * @return array
     */
    public function getData()
    {
        return $this->arrData;
    }

    /**
     * Set the template name.
     *
     * @param string $strTemplate The new name.
     *
     * @return void
     */
    public function setName($strTemplate)
    {
        $this->strTemplate = $strTemplate;
    }

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

    /**
     * Print all template variables to the screen using print_r.
     *
     * @return void
     */
    public function showTemplateVars()
    {
        echo "
\n";
        // @codingStandardsIgnoreStart - We really want to keep this debug function here.
        print_r($this->arrData);
        // @codingStandardsIgnoreEnd
        echo "
\n";
    }

    /**
     * Print all template variables to the screen using var_dump.
     *
     * @return void
     */
    public function dumpTemplateVars()
    {
        echo "
\n";
        // @codingStandardsIgnoreStart - We really want to keep this debug function here.
        var_dump($this->arrData);
        // @codingStandardsIgnoreEnd
        echo "
\n";
    }

    /**
     * Find a particular template file and return its path.
     *
     * @param string $strTemplate       Name of the template file.
     *
     * @param string $strFormat         The format to search for.
     *
     * @param bool   $blnFailIfNotFound Boolean flag telling if an Exception shall be thrown when the file can not
     *                                  be found.
     *
     * @throws \RuntimeException When the flag has been set and the file has not been found.
     *
     * @return string
     *
     * @SuppressWarnings(PHPMD.Superglobals)
     * @SuppressWarnings(PHPMD.CamelCaseVariableName)
     */
    protected function getTemplate($strTemplate, $strFormat = 'html5', $blnFailIfNotFound = false)
    {
        $strTemplate = basename($strTemplate);
        $strCustom   = 'templates';
        // Check for a theme folder.
        if (TL_MODE == 'FE') {
            $tmpDir = str_replace('../', '', $GLOBALS['objPage']->templateGroup);
            if (!empty($tmpDir)) {
                $strCustom = $tmpDir;
            }
        }

        try {
            return \TemplateLoader::getPath($strTemplate, $strFormat, $strCustom);
        } catch (\Exception $exception) {
            if ($blnFailIfNotFound) {
                throw new \RuntimeException(
                    sprintf('Could not find template %s.%s', $strTemplate, $strFormat),
                    1,
                    $exception
                );
            }
        }

        return null;
    }

    /**
     * Call the parse Template HOOK.
     *
     * @return void
     *
     * @SuppressWarnings(PHPMD.Superglobals)
     * @SuppressWarnings(PHPMD.CamelCaseVariableName)
     */
    protected function callParseTemplateHook()
    {
        if (isset($GLOBALS['METAMODEL_HOOKS']['parseTemplate'])
            && is_array($GLOBALS['METAMODEL_HOOKS']['parseTemplate'])
        ) {
            foreach ($GLOBALS['METAMODEL_HOOKS']['parseTemplate'] as $callback) {
                list($strClass, $strMethod) = $callback;

                $objCallback = (in_array('getInstance', get_class_methods($strClass)))
                    ? call_user_func(array($strClass, 'getInstance'))
                    : new $strClass();

                $objCallback->$strMethod($this);
            }
        }
    }

    /**
     * Parse the template file and return it as string.
     *
     * @param string  $strOutputFormat   The desired output format.
     *
     * @param boolean $blnFailIfNotFound If set to true, the template object will throw an exception if the template
     *                                   can not be found. Defaults to false.
     *
     * @return string The parsed template.
     */
    public function parse($strOutputFormat, $blnFailIfNotFound = false)
    {
        if ($this->strTemplate == '') {
            return '';
        }

        // Set the format.
        $this->strFormat = $strOutputFormat;

        // HOOK: add custom parse filters.
        $this->callParseTemplateHook();

        $strBuffer = '';

        // Start with the template itself
        $this->strParent = $this->strTemplate;

        // Include the parent templates
        while ($this->strParent !== null) {
            $strCurrent = $this->strParent;
            $strParent  = $this->strDefault
                ?: $this->getTemplate($this->strParent, $this->strFormat, $blnFailIfNotFound);

            // Check if we have the template.
            if (empty($strParent)) {
                return sprintf(
                    'Template %s not found (it is maybe within a unreachable theme folder?).',
                    $this->strParent
                );
            }

            // Reset the flags
            $this->strParent  = null;
            $this->strDefault = null;

            ob_start();
            include($strParent);

            // Capture the output of the root template
            if ($this->strParent === null) {
                $strBuffer = ob_get_contents();
            } elseif ($this->strParent == $strCurrent) {
                $this->strDefault = $this->getTemplate($this->strParent, $this->strFormat, $blnFailIfNotFound);
            }

            ob_end_clean();
        }

        // Reset the internal arrays
        $this->arrBlocks = array();

        // Add start and end markers in debug mode
        if (\Config::get('debugMode') && in_array($this->strFormat, ['html5', 'xhtml'])) {
            $strRelPath = str_replace(TL_ROOT . '/', '', $this->getTemplate($this->strTemplate, $this->strFormat));
            $strBuffer  = <<<EOF

$strBuffer

EOF;
        }

        return $strBuffer;
    }

    /**
     * Protected as only the included template file shall be able to call.
     *
     * This is needed to remain protected, as outside from {@link Template::parse()} the format is undefined.
     *
     * @return string
     */
    protected function getFormat()
    {
        return $this->strFormat;
    }

    /**
     * Static convenience method to perform the whole rendering within one line of code.
     *
     * @param string $strTemplate       Name of the template file.
     *
     * @param string $strOutputFormat   The desired output format.
     *
     * @param array  $arrTplData        The data to use in the template.
     *
     * @param bool   $blnFailIfNotFound If set to true, the template object will throw an exception if the template
     *                                  can not be found. Defaults to false.
     *
     * @return string
     */
    public static function render($strTemplate, $strOutputFormat, $arrTplData, $blnFailIfNotFound = false)
    {
        $objTemplate = new self($strTemplate);
        $objTemplate->setData($arrTplData);

        return $objTemplate->parse($strOutputFormat, $blnFailIfNotFound);
    }

    /**
     * Extend another template
     *
     * @param string $strName The template name.
     *
     * @return void
     */
    public function extend($strName)
    {
        $this->strParent = $strName;
    }

    /**
     * Insert the content of the parent block
     *
     * @return void
     */
    public function parent()
    {
        echo '[[TL_PARENT]]';
    }

    /**
     * Start a new block
     *
     * @param string $strName The block name.
     *
     * @return void
     *
     * @throws \Exception If a child templates contains nested blocks.
     */
    public function block($strName)
    {
        $this->arrBlockNames[] = $strName;

        // Root template
        if ($this->strParent === null) {
            // Register the block name
            if (!isset($this->arrBlocks[$strName])) {
                $this->arrBlocks[$strName] = '[[TL_PARENT]]';
            } elseif (is_array($this->arrBlocks[$strName])) {
                // Combine the contents of the child blocks
                $callback = function ($current, $parent) {
                    return str_replace('[[TL_PARENT]]', $parent, $current);
                };

                $this->arrBlocks[$strName] = array_reduce($this->arrBlocks[$strName], $callback, '[[TL_PARENT]]');
            }

            // Handle nested blocks
            if ($this->arrBlocks[$strName] != '[[TL_PARENT]]') {
                // Output everything before the first TL_PARENT tag
                if (strpos($this->arrBlocks[$strName], '[[TL_PARENT]]') !== false) {
                    list($content) = explode('[[TL_PARENT]]', $this->arrBlocks[$strName], 2);
                    echo $content;
                } else {
                    // Output the current block and start a new output buffer to remove the following blocks
                    echo $this->arrBlocks[$strName];
                    ob_start();
                }
            }
        } else {
            // Child template
            // Clean the output buffer
            ob_end_clean();

            // Check for nested blocks
            if (count($this->arrBlockNames) > 1) {
                throw new \Exception('Nested blocks are not allowed in child templates');
            }

            // Start a new output buffer
            ob_start();
        }
    }

    /**
     * End a block
     *
     * @return void
     *
     * @throws \Exception If there is no open block.
     */
    public function endblock()
    {
        // Check for open blocks
        if (empty($this->arrBlockNames)) {
            throw new \Exception('You must start a block before you can end it');
        }

        // Get the block name
        $name = array_pop($this->arrBlockNames);

        // Root template
        if ($this->strParent === null) {
            // Handle nested blocks
            if ($this->arrBlocks[$name] != '[[TL_PARENT]]') {
                // Output everything after the first TL_PARENT tag
                if (strpos($this->arrBlocks[$name], '[[TL_PARENT]]') !== false) {
                    list(, $content) = explode('[[TL_PARENT]]', $this->arrBlocks[$name], 2);
                    echo $content;
                } else {
                    // Remove the overwritten content
                    ob_end_clean();
                }
            }
        } else {
            // Child template
            // Capture the block content
            $this->arrBlocks[$name][] = ob_get_contents();
            ob_end_clean();

            // Start a new output buffer
            ob_start();
        }
    }

    /**
     * Insert a template
     *
     * @param string $strName The template name.
     * @param array  $arrData An optional data array.
     *
     * @return void
     */
    public function insert($strName, array $arrData = null)
    {
        if (TL_MODE == 'BE') {
            $objTemplate = new \BackendTemplate($strName);
        } else {
            $objTemplate = new \FrontendTemplate($strName);
        }

        if ($arrData !== null) {
            $objTemplate->setData($arrData);
        }

        echo $objTemplate->parse($this->getFormat());
    }
}


<?php

/**
 * This file is part of MetaModels/core.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/core
 * @author     Ingolf Steinhardt <info@e-spin.de>
 * @author     Sven Baumann <baumann.sv@gmail.com>
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\BackendIntegration;

use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents;
use ContaoCommunityAlliance\Contao\Bindings\Events\System\LogEvent;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * Purge the MetaModels cache.
 */
class PurgeAssets
{
    /**
     * Purge the page cache.
     *
     * @return void
     *
     * @SuppressWarnings(PHPMD.Superglobals)
     * @SuppressWarnings(PHPMD.CamelCaseVariableName)
     */
    public function purge()
    {
        foreach ($GLOBALS['TL_PURGE']['folders']['metamodels_assets']['affected'] as $folderName) {
            // Purge the folder
            $folder = new \Folder($folderName);
            $folder->purge();
        }

        /** @var EventDispatcherInterface $dispatcher */
        $dispatcher = $GLOBALS['container']['event-dispatcher'];
        $dispatcher->dispatch(
            ContaoEvents::SYSTEM_LOG,
            new LogEvent('Purged the MetaModels assets', __METHOD__, TL_CRON)
        );
    }
}


<?php

/**
 * This file is part of MetaModels/core.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/core
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     Sven Baumann <baumann.sv@gmail.com>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\DcaSettingCondition;

use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator;
use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ModelToLabelEvent;
use ContaoCommunityAlliance\DcGeneral\Event\AbstractEnvironmentAwareEvent;
use Doctrine\DBAL\Connection;
use MetaModels\CoreBundle\Assets\IconBuilder;
use MetaModels\IFactory;
use Symfony\Component\Translation\TranslatorInterface;

/**
 * This handles the rendering of models to labels.
 */
class ModelToLabelListener extends AbstractListener
{
    /**
     * The translator.
     *
     * @var TranslatorInterface
     */
    private $translator;

    /**
     * The icon builder.
     *
     * @var IconBuilder
     */
    private $iconBuilder;

    /**
     * Create a new instance.
     *
     * @param RequestScopeDeterminator $scopeDeterminator The scope determinator.
     * @param IFactory                 $factory           The MetaModel factory.
     * @param Connection               $connection        The database connection.
     * @param TranslatorInterface      $translator        The translator.
     * @param IconBuilder              $iconBuilder       The icon builder.
     */
    public function __construct(
        RequestScopeDeterminator $scopeDeterminator,
        IFactory $factory,
        Connection $connection,
        TranslatorInterface $translator,
        IconBuilder $iconBuilder
    ) {
        parent::__construct($scopeDeterminator, $factory, $connection);
        $this->translator  = $translator;
        $this->iconBuilder = $iconBuilder;
    }

    /**
     * Render the html for the input screen condition.
     *
     * @param ModelToLabelEvent $event The event.
     *
     * @return void
     *
     * @SuppressWarnings(PHPMD.Superglobals)
     * @SuppressWarnings(PHPMD.CamelCaseVariableName)
     */
    public function handle(ModelToLabelEvent $event)
    {
        if (!$this->wantToHandle($event)) {
            return;
        }

        $environment    = $event->getEnvironment();
        $model          = $event->getModel();
        $metaModel      = $this->getMetaModel($environment);
        $attribute      = $metaModel->getAttributeById($model->getProperty('attr_id'));
        $type           = $model->getProperty('type');
        $parameterValue = (\is_array($model->getProperty('value'))
            ? implode(', ', $model->getProperty('value'))
            : $model->getProperty('value'));

        $name = $this->translator->trans(
            'tl_metamodel_dcasetting_condition.conditionnames.' . $type,
            [],
            'contao_tl_metamodel_dcasetting_condition'
        );

        $event
            ->setLabel($this->getLabelText($type))
            ->setArgs([
                $this->iconBuilder->getBackendIconImageTag(
                    'bundles/metamodelscore/images/icons/filter_default.png',
                    $name,
                    '',
                    'bundles/metamodelscore/images/icons/filter_default.png'
                ),
                $name,
                $attribute ? $attribute->getName() : '' . $model->getProperty('attr_id'),
                $parameterValue
            ]);
    }

    /**
     * {@inheritDoc}
     */
    protected function wantToHandle(AbstractEnvironmentAwareEvent $event)
    {
        return $event->getEnvironment()->getInputProvider()->hasParameter('mode')
            ? parent::wantToHandle($event)
              && ('select' === $event->getEnvironment()->getInputProvider()->getParameter('act'))
            : parent::wantToHandle(
                $event
            );
    }

    /**
     * Retrieve the label text for a condition setting or the default one.
     *
     * @param string $type The type of the element.
     *
     * @return string
     */
    private function getLabelText($type)
    {
        $label = $this->translator->trans(
            'tl_metamodel_dcasetting_condition.typedesc.' . $type,
            [],
            'contao_tl_metamodel_dcasetting_condition'
        );
        if ($label === 'tl_metamodel_dcasetting_condition.typedesc.' . $type) {
            $label = $this->translator->trans(
                'tl_metamodel_dcasetting_condition.typedesc._default_',
                [],
                'contao_tl_metamodel_dcasetting_condition'
            );
            if ($label === 'tl_metamodel_dcasetting_condition.typedesc._default_') {
                return $type;
            }
        }
        return $label;
    }
}


<?php

/**
 * This file is part of MetaModels/core.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/core
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     Sven Baumann <baumann.sv@gmail.com>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\MetaModel;

use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\DecodePropertyValueForWidgetEvent;
use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\EncodePropertyValueFromWidgetEvent;

/**
 * This handles the mangling of the language array.
 */
class LanguageArrayListener extends AbstractAbstainingListener
{
    /**
     * Decode a language array.
     *
     * @param DecodePropertyValueForWidgetEvent $event The event.
     *
     * @return void
     */
    public function handleDecode(DecodePropertyValueForWidgetEvent $event)
    {
        if (!$this->wantToHandle($event) || ($event->getProperty() !== 'languages')) {
            return;
        }

        $langValues = (array) $event->getValue();
        $output     = [];
        foreach ($langValues as $langCode => $subValue) {
            if (is_array($subValue)) {
                $output[] = array_merge($subValue, ['langcode' => $langCode]);
            }
        }

        $event->setValue($output);
    }

    /**
     * Decode a language array.
     *
     * @param EncodePropertyValueFromWidgetEvent $event The event.
     *
     * @return void
     */
    public function handleEncode(EncodePropertyValueFromWidgetEvent $event)
    {
        if (!$this->wantToHandle($event) || ($event->getProperty() !== 'languages')) {
            return;
        }

        $langValues  = (array) $event->getValue();
        $hasFallback = false;
        $output      = [];
        foreach ($langValues as $subValue) {
            $langCode = $subValue['langcode'];
            unset($subValue['langcode']);

            // We clear all subsequent fallbacks after we have found one.
            if ($hasFallback) {
                $subValue['isfallback'] = '';
            }

            if ($subValue['isfallback']) {
                $hasFallback = true;
            }

            $output[$langCode] = $subValue;
        }

        // If no fallback has been set, use the first language available.
        if ((!$hasFallback) && count($output)) {
            $output[$langValues[0]['langcode']]['isfallback'] = '1';
        }

        $event->setValue($output);
    }
}


<?php

/**
 * This file is part of MetaModels/core.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/core
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     David Molineus <david.molineus@netzmacht.de>
 * @author     Sven Baumann <baumann.sv@gmail.com>
 * @author     Richard Henkenjohann <richardhenkenjohann@googlemail.com>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\Helper;

use Contao\Environment;
use Contao\System;
use MetaModels\Events\MetaModelsBootEvent;
use MetaModels\MetaModelsEvents;

/**
 * Base event listener to boot up a MetaModelServiceContainer.
 */
class SubSystemBoot
{
    /**
     * Boot up the system.
     *
     * @return void
     */
    public function boot()
    {
        /** @var Environment $environment */
        $environment = System::getContainer()->get('contao.framework')->getAdapter(Environment::class);
        $script      = explode('?', $environment->get('relativeRequest'), 2)[0];

        // There is no need to boot in login or install screen.
        if (('contao/login' === $script) || ('contao/install' === $script)) {
            return;
        }

        // Ensure all tables are created.
        $connection = System::getContainer()->get('database_connection');
        if (!$connection->getSchemaManager()->tablesExist(
            [
                'tl_metamodel',
                'tl_metamodel_dca',
                'tl_metamodel_dca_sortgroup',
                'tl_metamodel_dcasetting',
                'tl_metamodel_dcasetting_condition',
                'tl_metamodel_attribute',
                'tl_metamodel_filter',
                'tl_metamodel_filtersetting',
                'tl_metamodel_rendersettings',
                'tl_metamodel_rendersetting',
                'tl_metamodel_dca_combine',
            ]
        )) {
            System::getContainer()
                ->get('logger')
                ->error('MetaModels startup interrupted. Not all MetaModels tables have been created.');
            return;
        }

        $event = new MetaModelsBootEvent();
        $this->tryDispatch(MetaModelsEvents::SUBSYSTEM_BOOT, $event);

        $determinator = System::getContainer()->get('cca.dc-general.scope-matcher');
        switch (true) {
            case $determinator->currentScopeIsFrontend():
                $this->tryDispatch(MetaModelsEvents::SUBSYSTEM_BOOT_FRONTEND, $event);
                break;
            case $determinator->currentScopeIsBackend():
                $this->tryDispatch(MetaModelsEvents::SUBSYSTEM_BOOT_BACKEND, $event);
                break;
            default:
        }
    }

    /**
     * Test if the event must get dispatched, if so, trigger deprecation and dispatch then.
     *
     * @param string              $eventName The event name.
     * @param MetaModelsBootEvent $event     The event payload.
     *
     * @return void
     */
    private function tryDispatch($eventName, MetaModelsBootEvent $event)
    {
        $dispatcher = System::getContainer()->get('event_dispatcher');
        if ($dispatcher->hasListeners($eventName)) {
            // @codingStandardsIgnoreStart
            @trigger_error('Event "' . $eventName . '" has been deprecated - Use registered services.', E_USER_DEPRECATED);
            // @codingStandardsIgnoreEnd
            $dispatcher->dispatch($eventName, $event);
        }
    }
}


<?php

/**
 * This file is part of MetaModels/core.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/core
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     Sven Baumann <baumann.sv@gmail.com>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\DcaCombine;

use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\EncodePropertyValueFromWidgetEvent;

/**
 * This class optimizes the sorting values.
 */
class FixSortingListener
{
    /**
     * Handle event to update the sorting for DCA combinations.
     *
     * @param EncodePropertyValueFromWidgetEvent $event The event.
     *
     * @return void
     */
    public function handle(EncodePropertyValueFromWidgetEvent $event)
    {
        if (('tl_metamodel_dca_combine' !== $event->getEnvironment()->getDataDefinition()->getName())
            || ('rows' !== $event->getProperty())) {
            return;
        }

        $values = $event->getValue();
        $index  = 0;
        $time   = time();
        foreach (array_keys($values) as $key) {
            $values[$key]['sorting'] = $index;
            $values[$key]['tstamp']  = $time;

            $index += 128;
        }

        $event->setValue($values);
    }
}


<?php

/**
 * This file is part of MetaModels/core.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/core
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     David Maack <david.maack@arcor.de>
 * @author     Jan Malte Gerth <anmeldungen@malte-gerth.de>
 * @author     Oliver Hoff <oliver@hofff.com>
 * @author     Sven Baumann <baumann.sv@gmail.com>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\Filter\Setting;

use Contao\Input;
use Contao\InsertTags;
use Contao\Session;
use Doctrine\DBAL\Connection;
use MetaModels\Filter\IFilter;
use MetaModels\Filter\Rules\SimpleQuery;
use MetaModels\FrontendIntegration\FrontendFilterOptions;
use MetaModels\IItem;
use MetaModels\IMetaModelsServiceContainer;
use MetaModels\Render\Setting\ICollection as IRenderSettings;
use Psr\Container\ContainerInterface;
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;

/**
 * This filter condition generates a filter rule for a predefined SQL query.
 * The generated rule will only return ids that are returned from this query.
 */
class CustomSql implements ISimple, ServiceSubscriberInterface
{
    /**
     * The parenting filter setting container this setting belongs to.
     *
     * @var ICollection
     */
    private $collection = null;

    /**
     * The attributes of this filter setting.
     *
     * @var array
     */
    private $data = [];

    /**
     * The filter params (should be array or null).
     *
     * @var array
     */
    private $filterParameters;

    /**
     * The query string.
     *
     * @var string
     */
    private $queryString;

    /**
     * The query parameters.
     *
     * @var array
     */
    private $queryParameter;

    /**
     * The service container.
     *
     * @var ContainerInterface
     */
    private $container;

    /**
     * Constructor - initialize the object and store the parameters.
     *
     * @param ICollection        $collection   The parenting filter settings object.
     * @param array              $data         The attributes for this filter setting.
     * @param ContainerInterface $container    The service container.
     *
     * @throws \InvalidArgumentException When a service is missing.
     */
    public function __construct($collection, $data, ContainerInterface $container)
    {
        $this->collection = $collection;
        $this->data       = $data;

        $missing = [];
        foreach (array_keys(self::getSubscribedServices()) as $serviceId) {
            if (!$container->has($serviceId)) {
                $missing[] = $serviceId;
            }
        }
        if (!empty($missing)) {
            throw new \InvalidArgumentException(
                'The service container is missing the following services: ' . implode(', ', $missing)
            );
        }
        $this->container = $container;
    }

    /**
     * Get the needed services.
     *
     * @return array
     */
    public static function getSubscribedServices()
    {
        return [
            Connection::class => Connection::class,
            Input::class      => Input::class,
            InsertTags::class => InsertTags::class,
            Session::class    => Session::class,
            // This one is deprecated.
            IMetaModelsServiceContainer::class => IMetaModelsServiceContainer::class
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function get($strKey)
    {
        return isset($this->data[$strKey]) ? $this->data[$strKey] : null;
    }

    /**
     * {@inheritdoc}
     */
    public function prepareRules(IFilter $objFilter, $arrFilterUrl)
    {
        $this->filterParameters = $arrFilterUrl;
        $this->queryString      = $this->get('customsql');
        $this->queryParameter   = [];

        $objFilter->addFilterRule($this->getFilterRule());

        unset($this->filterParameters);
        unset($this->queryString);
        unset($this->queryParameter);
    }

    /**
     * {@inheritdoc}
     */
    public function generateFilterUrlFrom(IItem $objItem, IRenderSettings $objRenderSetting)
    {
        return [];
    }

    /**
     * {@inheritDoc}
     */
    public function getParameters()
    {
        $arrParams = [];

        preg_match_all('@\{\{param::filter\?([^}]*)\}\}@', $this->get('customsql'), $arrMatches);
        foreach ($arrMatches[1] as $strQuery) {
            parse_str($strQuery, $arrArgs);
            if (isset($arrArgs['name'])) {
                $arrName     = (array) $arrArgs['name'];
                $arrParams[] = $arrName[0];
            }
        }

        return $arrParams;
    }

    /**
     * {@inheritdoc}
     */
    public function getParameterDCA()
    {
        return [];
    }

    /**
     * {@inheritdoc}
     */
    public function getParameterFilterNames()
    {
        return [];
    }

    /**
     * {@inheritdoc}
     */
    public function getParameterFilterWidgets(
        $arrIds,
        $arrFilterUrl,
        $arrJumpTo,
        FrontendFilterOptions $objFrontendFilterOptions
    ) {
        return [];
    }

    /**
     * {@inheritdoc}
     */
    public function getReferencedAttributes()
    {
        return [];
    }

    /**
     * Compile the query and the parameters.
     *
     * @return void
     */
    private function compile()
    {
        $this->parseTable();
        $this->parseRequestVars();
        $this->parseSecureInsertTags();
        $this->parseInsertTags();
    }

    /**
     * Retrieve the simple query.
     *
     * @return SimpleQuery
     */
    private function getFilterRule()
    {
        $this->compile();

        return new SimpleQuery(
            $this->queryString,
            $this->queryParameter,
            'id',
            $this->container->get(Connection::class)
        );
    }

    /**
     * Add parameters to the list.
     *
     * @param array $parameters The parameters to add.
     *
     * @return void
     */
    private function addParameters($parameters)
    {
        if (empty($parameters)) {
            return;
        }

        $this->queryParameter = array_merge($this->queryParameter, $parameters);
    }

    /**
     * Add a parameter to the list.
     *
     * @param string $parameter The parameter to add.
     *
     * @return void
     */
    private function addParameter($parameter)
    {
        $this->queryParameter[] = $parameter;
    }

    /**
     * Replace the table name in the query string.
     *
     * @return void
     */
    private function parseTable()
    {
        $this->queryString = str_replace(
            '{{table}}',
            $this->collection->getMetaModel()->getTableName(),
            $this->queryString
        );
    }

    /**
     * Retrieve the value with the given name from the service container.
     *
     * @param string $valueName The name of the value in the source to retrieve.
     *
     * @param array  $arguments The arguments of the parameter.
     *
     * @return mixed
     */
    private function getValueFromServiceContainer($valueName, $arguments)
    {
        if (!empty($arguments['service'])) {
            $serviceName = $arguments['service'];
        } else {
            $serviceName = $valueName;
        }

        $service = $this->container->get(IMetaModelsServiceContainer::class)->getService($serviceName);
        if (is_callable($service)) {
            return call_user_func($service, $valueName, $arguments);
        }

        return 'NULL';
    }

    /**
     * Retrieve the value with the given name from the source with the given name.
     *
     * @param string $source    The source to retrieve the value from.
     *                          Valid values are: ('get', 'post', 'cookie', 'session', 'filter' or 'container').
     *
     * @param string $valueName The name of the value in the source to retrieve.
     *
     * @param array  $arguments The arguments of the parameter.
     *
     * @return mixed
     */
    private function getValueFromSource($source, $valueName, $arguments)
    {
        switch (strtolower($source)) {
            case 'get':
                return $this->container->get(Input::class)->get($valueName);

            case 'post':
                return $this->container->get(Input::class)->post($valueName);

            case 'cookie':
                return $this->container->get(Input::class)->cookie($valueName);

            case 'session':
                return $this->container->get(Session::class)->get($valueName);

            case 'filter':
                if (is_array($this->filterParameters)) {
                    if (array_key_exists($valueName, $this->filterParameters)) {
                        return $this->filterParameters[$valueName];
                    }

                    return null;
                }
                break;

            case 'container':
                // @codingStandardsIgnoreStart
                @trigger_error(
                    'Getting filter values from the service container is deprecated, the container will get removed.',
                    E_USER_DEPRECATED
                );
                // @codingStandardsIgnoreEnd
                return $this->getValueFromServiceContainer($valueName, $arguments);

            default:
        }

        // Unknown sources always resort to null.
        return null;
    }

    /**
     * Convert a parameter using an aggregate function.
     *
     * @param string $var       The parameter value.
     *
     * @param array  $arguments The arguments of the parameter.
     *
     * @return string
     */
    private function convertParameterAggregate($var, $arguments)
    {
        // Treat as list.
        $var = (array) $var;

        if (!empty($arguments['recursive'])) {
            $var = iterator_to_array(
                new \RecursiveIteratorIterator(
                    new \RecursiveArrayIterator(
                        $var
                    )
                )
            );
        }

        if (!$var) {
            return 'NULL';
        }

        if (!empty($arguments['key'])) {
            $var = array_keys($var);
        } else {
            // Use values.
            $var = array_values($var);
        }

        if ($arguments['aggregate'] == 'set') {
            $this->addParameter(implode(',', $var));

            return '?';
        }

        $this->addParameters($var);

        return rtrim(str_repeat('?,', count($var)), ',');
    }

    /**
     * Convert a parameter in the query string.
     *
     * @param array $arrMatch The match from the preg_replace_all call in parseRequestVars().
     *
     * @return string
     *
     * @internal Only to be used via parseRequestVars().
     */
    public function convertParameter($arrMatch)
    {
        list($strSource, $strQuery) = explode('?', $arrMatch[1], 2);
        parse_str($strQuery, $arrArgs);
        $arrName = (array) $arrArgs['name'];

        $var = $this->getValueFromSource($strSource, array_shift($arrName), $arrArgs);

        $index = 0;
        $count = count($arrName);
        while ($index < $count && is_array($var)) {
            $var = $var[$arrName[$index++]];
        }

        if ($index != $count || $var === null) {
            if (array_key_exists('default', $arrArgs) && (null !== $arrArgs['default'])) {
                $this->addParameter($arrArgs['default']);

                return '?';
            } else {
                return 'NULL';
            }
        }

        // Treat as scalar value.
        if (!isset($arrArgs['aggregate'])) {
            $this->addParameter($var);

            return '?';
        }

        return $this->convertParameterAggregate($var, $arrArgs);
    }

    /**
     * Parse a request var insert tag within the SQL.
     *
     * @return void
     */
    private function parseRequestVars()
    {
        $this->queryString = preg_replace_callback(
            '@\{\{param::([^}]*)\}\}@',
            [$this, 'convertParameter'],
            $this->queryString
        );
    }

    /**
     * Replace all insert tags in the query string.
     *
     * @param string $queryString The string to replace insert tags within.
     *
     * @return string
     */
    private function parseInsertTagsInternal($queryString)
    {
        return $this->container->get(InsertTags::class)->replace($queryString, false);
    }

    /**
     * Replace all insert tags in the query string.
     *
     * @param string $arrMatch The match from the preg_replace call.
     *
     * @return string
     *
     * @internal Only to be used internal as callback from parseSecureInsertTags().
     */
    public function parseAndAddSecureInsertTagAsParameter($arrMatch)
    {
        $this->addParameter($this->parseInsertTagsInternal('{{' . $arrMatch[1] . '}}'));

        return '?';
    }

    /**
     * Replace all secure insert tags.
     *
     * @return void
     */
    private function parseSecureInsertTags()
    {
        $this->queryString = preg_replace_callback(
            '@\{\{secure::([^}]+)\}\}@',
            [$this, 'parseAndAddSecureInsertTagAsParameter'],
            $this->queryString
        );
    }

    /**
     * Replace all insert tags in the query string.
     *
     * @return void
     */
    private function parseInsertTags()
    {
        $this->queryString = $this->parseInsertTagsInternal($this->queryString);
    }
}


<?php

/**
 * This file is part of MetaModels/core.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/core
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     Sven Baumann <baumann.sv@gmail.com>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\CoreBundle\EventListener\DcGeneral\Breadcrumb;

use Doctrine\DBAL\Connection;

/**
 * This class provides obtaining the connection.
 */
trait ConnectionTrait
{
    /**
     * The connection.
     *
     * @var Connection
     */
    private $connection;

    /**
     * Set the database connection.
     *
     * @param Connection $connection The database connection.
     *
     * @return void
     */
    public function setConnection(Connection $connection)
    {
        $this->connection = $connection;
    }

    /**
     * Retrieve the row.
     *
     * @param string $rowId The id.
     *
     * @param string $table The table to fetch from.
     *
     * @return object
     */
    private function getRow($rowId, $table)
    {
        return (object) $this->connection
            ->createQueryBuilder()
            ->select('*')
            ->from($table)
            ->where('id=:id')
            ->setParameter('id', $rowId)
            ->setMaxResults(1)
            ->execute()
            ->fetch(\PDO::FETCH_ASSOC);
    }
}