From 21254b048d8bc5db234273ceb43a467c8ce26bd7 Mon Sep 17 00:00:00 2001 From: jo Date: Thu, 11 Aug 2022 10:43:16 +0200 Subject: [PATCH] feat(legacy): setup config schema validation BREAKING CHANGE: Unrecognized values in the configuration file will raise validation errors, please make sure to cleanup your configuration file. --- legacy/.php-cs-fixer.php | 1 + legacy/application/configs/conf.php | 248 ++++++++--- legacy/composer.json | 3 +- legacy/composer.lock | 621 ++++++++++++++++++++++++---- 4 files changed, 723 insertions(+), 150 deletions(-) diff --git a/legacy/.php-cs-fixer.php b/legacy/.php-cs-fixer.php index a78761bd4..1e1134f66 100644 --- a/legacy/.php-cs-fixer.php +++ b/legacy/.php-cs-fixer.php @@ -2,6 +2,7 @@ $finder = PhpCsFixer\Finder::create() ->in(__DIR__) + ->notPath('application/configs/conf.php') ->exclude('application/models/airtime/map') ->exclude('application/models/airtime/om'); diff --git a/legacy/application/configs/conf.php b/legacy/application/configs/conf.php index 855172064..1d0990007 100644 --- a/legacy/application/configs/conf.php +++ b/legacy/application/configs/conf.php @@ -9,101 +9,217 @@ require_once VENDOR_PATH . '/autoload.php'; // THIS FILE IS NOT MEANT FOR CUSTOMIZING. use League\Uri\Contracts\UriException; use League\Uri\Uri; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Config\Definition\Processor; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; + +class Schema implements ConfigurationInterface +{ + public function getConfigTreeBuilder() + { + $trim_trailing_slash = function ($v) { + return rtrim($v, '/') . '/'; + }; + + $treeBuilder = new TreeBuilder(''); + $treeBuilder->getRootNode() + ->children() + + // General schema + ->arrayNode('general') + /**/->addDefaultsIfNotSet() + /**/->children() + /* */->scalarNode('public_url')->cannotBeEmpty()->end() + /* */->scalarNode('api_key')->cannotBeEmpty()->end() + /* */->arrayNode('allowed_cors_origins')->scalarPrototype()->defaultValue([])->end()->end() + /* */->scalarNode('dev_env')->defaultValue('production')->end() + /* */->scalarNode('auth')->defaultValue('local')->end() + /* */->integerNode('cache_ahead_hours')->defaultValue(1)->end() + /**/->end() + ->end() + + // Database schema + ->arrayNode('database') + /**/->addDefaultsIfNotSet() + /**/->children() + /* */->scalarNode('host')->defaultValue('localhost')->end() + /* */->integerNode('port')->defaultValue(5432)->end() + /* */->scalarNode('name')->defaultValue('libretime')->end() + /* */->scalarNode('user')->defaultValue('libretime')->end() + /* */->scalarNode('password')->defaultValue('libretime')->end() + /**/->end() + ->end() + + // Rabbitmq schema + ->arrayNode('rabbitmq') + /**/->addDefaultsIfNotSet() + /**/->children() + /* */->scalarNode('host')->defaultValue('localhost')->end() + /* */->integerNode('port')->defaultValue(5672)->end() + /* */->scalarNode('vhost')->defaultValue('/libretime')->end() + /* */->scalarNode('user')->defaultValue('libretime')->end() + /* */->scalarNode('password')->defaultValue('libretime')->end() + /**/->end() + ->end() + + // Storage schema + ->arrayNode('storage') + /**/->addDefaultsIfNotSet() + /**/->children() + /* */->scalarNode('path')->defaultValue('/srv/libretime') + /* */->validate()->ifString()->then($trim_trailing_slash)->end() + /* */->end() + /**/->end() + ->end() + + // Facebook schema + ->arrayNode('facebook') + /**/->setDeprecated("legacy", "3.0.0-alpha.11") + /**/->children() + /* */->scalarNode('facebook_app_id')->end() + /* */->scalarNode('facebook_app_url')->end() + /* */->scalarNode('facebook_app_api_key')->end() + /**/->end() + ->end() + + // LDAP schema + ->arrayNode('ldap') + /**/->children() + /* */->scalarNode('hostname')->end() + /* */->scalarNode('binddn')->end() + /* */->scalarNode('password')->end() + /* */->scalarNode('account_domain')->end() + /* */->scalarNode('basedn')->end() + /* */->scalarNode('groupmap_guest')->end() + /* */->scalarNode('groupmap_host')->end() + /* */->scalarNode('groupmap_program_manager')->end() + /* */->scalarNode('groupmap_admin')->end() + /* */->scalarNode('groupmap_superadmin')->end() + /* */->scalarNode('filter_field')->end() + /**/->end() + ->end() + + // Playout schema + ->arrayNode('playout') + /**/->ignoreExtraKeys() + ->end() + + ->end(); + + return $treeBuilder; + } +} class Config { - private static $CC_CONFIG; + private static $internal_values; + private static $values; - public static function loadConfig() + private static function load() { $filename = $_SERVER['LIBRETIME_CONFIG_FILEPATH'] ?? LIBRETIME_CONFIG_FILEPATH; - $values = yaml_parse_file($filename); + $dirty = yaml_parse_file($filename); - $CC_CONFIG = []; + $schema = new Schema(); + $processor = new Processor(); + try { + $values = $processor->processConfiguration($schema, [$dirty]); + } catch (InvalidConfigurationException $error) { + echo "could not parse configuration: " . $error->getMessage(); + exit; + } + + // Public url + $public_url = self::validateUrl('general.public_url', $values['general']['public_url']); + $values['general']['public_url_raw'] = $public_url; + $values['general']['public_url'] = strval($public_url); + + // Allowed cors origins + $values['general']['allowed_cors_origins'][] = strval($values['general']['public_url_raw']->withPath('')); + + // Storage path + if (!is_dir($values['storage']['path'])) { + echo "the configured storage.path '{$values['storage']['path']}' does not exists!"; + exit; + } + if (!is_writable($values['storage']['path'])) { + echo "the configured storage.path '{$values['storage']['path']}' is not writable!"; + exit; + } + + self::$values = $values; + self::fillInternalValues($values); + } + + private static function fillInternalValues($values) + { + $internal_values = []; // General // ////////////////////////////////////////////////////////////////////////////// - $CC_CONFIG['apiKey'] = [$values['general']['api_key']]; - - $public_url = self::validateUrl('general.public_url', $values['general']['public_url']); - $CC_CONFIG['public_url_raw'] = $public_url; - $CC_CONFIG['public_url'] = strval($public_url); + $internal_values['apiKey'] = [$values['general']['api_key']]; + $internal_values['public_url_raw'] = $values['general']['public_url_raw']; + $internal_values['public_url'] = $values['general']['public_url']; // Allowed hosts - $CC_CONFIG['allowedCorsOrigins'] = $values['general']['allowed_cors_origins'] ?? []; - $CC_CONFIG['allowedCorsOrigins'][] = strval($public_url->withPath('')); + $internal_values['allowedCorsOrigins'] = $values['general']['allowed_cors_origins']; - $CC_CONFIG['dev_env'] = $values['general']['dev_env'] ?? 'production'; - $CC_CONFIG['auth'] = $values['general']['auth'] ?? 'local'; - $CC_CONFIG['cache_ahead_hours'] = $values['general']['cache_ahead_hours'] ?? 1; + $internal_values['dev_env'] = $values['general']['dev_env']; + $internal_values['auth'] = $values['general']['auth']; + $internal_values['cache_ahead_hours'] = $values['general']['cache_ahead_hours']; // SAAS remaining fields - $CC_CONFIG['stationId'] = $values['general']['station_id'] ?? ''; - $CC_CONFIG['phpDir'] = $values['general']['airtime_dir'] ?? ''; - $CC_CONFIG['staticBaseDir'] = $values['general']['static_base_dir'] ?? '/'; + $internal_values['stationId'] = ''; + $internal_values['phpDir'] = ''; + $internal_values['staticBaseDir'] = '/'; // Database // ////////////////////////////////////////////////////////////////////////////// - $CC_CONFIG['dsn']['phptype'] = 'pgsql'; - $CC_CONFIG['dsn']['host'] = $values['database']['host'] ?? 'localhost'; - $CC_CONFIG['dsn']['port'] = $values['database']['port'] ?? 5432; - $CC_CONFIG['dsn']['database'] = $values['database']['name'] ?? 'libretime'; - $CC_CONFIG['dsn']['username'] = $values['database']['user'] ?? 'libretime'; - $CC_CONFIG['dsn']['password'] = $values['database']['password'] ?? 'libretime'; + $internal_values['dsn']['phptype'] = 'pgsql'; + $internal_values['dsn']['host'] = $values['database']['host']; + $internal_values['dsn']['port'] = $values['database']['port']; + $internal_values['dsn']['database'] = $values['database']['name']; + $internal_values['dsn']['username'] = $values['database']['user']; + $internal_values['dsn']['password'] = $values['database']['password']; // RabbitMQ // ////////////////////////////////////////////////////////////////////////////// - $CC_CONFIG['rabbitmq']['host'] = $values['rabbitmq']['host'] ?? 'localhost'; - $CC_CONFIG['rabbitmq']['port'] = $values['rabbitmq']['port'] ?? 5672; - $CC_CONFIG['rabbitmq']['vhost'] = $values['rabbitmq']['vhost'] ?? '/libretime'; - $CC_CONFIG['rabbitmq']['user'] = $values['rabbitmq']['user'] ?? 'libretime'; - $CC_CONFIG['rabbitmq']['password'] = $values['rabbitmq']['password'] ?? 'libretime'; + $internal_values['rabbitmq']['host'] = $values['rabbitmq']['host']; + $internal_values['rabbitmq']['port'] = $values['rabbitmq']['port']; + $internal_values['rabbitmq']['vhost'] = $values['rabbitmq']['vhost']; + $internal_values['rabbitmq']['user'] = $values['rabbitmq']['user']; + $internal_values['rabbitmq']['password'] = $values['rabbitmq']['password']; // Storage // ////////////////////////////////////////////////////////////////////////////// - $CC_CONFIG['storagePath'] = $values['storage']['path'] ?? '/srv/libretime'; - if (!is_dir($CC_CONFIG['storagePath'])) { - echo "the configured storage.path '{$CC_CONFIG['storagePath']}' does not exists!"; - - exit; - } - if (!is_writable($CC_CONFIG['storagePath'])) { - echo "the configured storage.path '{$CC_CONFIG['storagePath']}' is not writable!"; - - exit; - } + $internal_values['storagePath'] = $values['storage']['path']; // Facebook (DEPRECATED) // ////////////////////////////////////////////////////////////////////////////// if (isset($values['facebook']['facebook_app_id'])) { - $CC_CONFIG['facebook-app-id'] = $values['facebook']['facebook_app_id']; - $CC_CONFIG['facebook-app-url'] = $values['facebook']['facebook_app_url']; - $CC_CONFIG['facebook-app-api-key'] = $values['facebook']['facebook_app_api_key']; + $internal_values['facebook-app-id'] = $values['facebook']['facebook_app_id']; + $internal_values['facebook-app-url'] = $values['facebook']['facebook_app_url']; + $internal_values['facebook-app-api-key'] = $values['facebook']['facebook_app_api_key']; } // LDAP // ////////////////////////////////////////////////////////////////////////////// if (array_key_exists('ldap', $values)) { - $CC_CONFIG['ldap_hostname'] = $values['ldap']['hostname']; - $CC_CONFIG['ldap_binddn'] = $values['ldap']['binddn']; - $CC_CONFIG['ldap_password'] = $values['ldap']['password']; - $CC_CONFIG['ldap_account_domain'] = $values['ldap']['account_domain']; - $CC_CONFIG['ldap_basedn'] = $values['ldap']['basedn']; - $CC_CONFIG['ldap_groupmap_guest'] = $values['ldap']['groupmap_guest']; - $CC_CONFIG['ldap_groupmap_host'] = $values['ldap']['groupmap_host']; - $CC_CONFIG['ldap_groupmap_program_manager'] = $values['ldap']['groupmap_program_manager']; - $CC_CONFIG['ldap_groupmap_admin'] = $values['ldap']['groupmap_admin']; - $CC_CONFIG['ldap_groupmap_superadmin'] = $values['ldap']['groupmap_superadmin']; - $CC_CONFIG['ldap_filter_field'] = $values['ldap']['filter_field']; + $internal_values['ldap_hostname'] = $values['ldap']['hostname']; + $internal_values['ldap_binddn'] = $values['ldap']['binddn']; + $internal_values['ldap_password'] = $values['ldap']['password']; + $internal_values['ldap_account_domain'] = $values['ldap']['account_domain']; + $internal_values['ldap_basedn'] = $values['ldap']['basedn']; + $internal_values['ldap_groupmap_guest'] = $values['ldap']['groupmap_guest']; + $internal_values['ldap_groupmap_host'] = $values['ldap']['groupmap_host']; + $internal_values['ldap_groupmap_program_manager'] = $values['ldap']['groupmap_program_manager']; + $internal_values['ldap_groupmap_admin'] = $values['ldap']['groupmap_admin']; + $internal_values['ldap_groupmap_superadmin'] = $values['ldap']['groupmap_superadmin']; + $internal_values['ldap_filter_field'] = $values['ldap']['filter_field']; } - // Demo - // ////////////////////////////////////////////////////////////////////////////// - if (isset($values['demo']['demo'])) { - $CC_CONFIG['demo'] = $values['demo']['demo']; - } - - self::$CC_CONFIG = $CC_CONFIG; + self::$internal_values = $internal_values; } public static function setAirtimeVersion() @@ -113,16 +229,16 @@ class Config // fallback to constant from constants.php if no other info is available $version = LIBRETIME_MAJOR_VERSION; } - self::$CC_CONFIG['airtime_version'] = trim($version); + self::$internal_values['airtime_version'] = trim($version); } public static function getConfig() { - if (is_null(self::$CC_CONFIG)) { - self::loadConfig(); + if (is_null(self::$internal_values)) { + self::load(); } - return self::$CC_CONFIG; + return self::$internal_values; } /** @@ -154,7 +270,7 @@ class Config $url = Uri::createFromString($value); return $url->withPath(rtrim($url->getPath() ?? '', '/') . '/'); - } catch (UriException|TypeError $e) { + } catch (UriException | TypeError $e) { echo "could not parse configuration field {$key}: " . $e->getMessage(); exit; @@ -163,7 +279,7 @@ class Config public static function getStoragePath() { - return rtrim(self::getConfig()['storagePath'], '/') . '/'; + return self::getConfig()['storagePath']; } public static function getPublicUrl() diff --git a/legacy/composer.json b/legacy/composer.json index 4faf20113..9412fe025 100644 --- a/legacy/composer.json +++ b/legacy/composer.json @@ -31,6 +31,7 @@ "league/uri": "6.4.0", "php-amqplib/php-amqplib": "^3.0", "simplepie/simplepie": "^1.5", + "symfony/config": "^5.4", "zf1s/zend-acl": "^1.13", "zf1s/zend-application": "^1.13", "zf1s/zend-auth": "^1.13", @@ -38,8 +39,8 @@ "zf1s/zend-controller": "^1.13", "zf1s/zend-date": "^1.13", "zf1s/zend-db": "^1.13", - "zf1s/zend-file-transfer": "^1.13", "zf1s/zend-file": "^1.13", + "zf1s/zend-file-transfer": "^1.13", "zf1s/zend-filter": "^1.13", "zf1s/zend-form": "^1.13", "zf1s/zend-http": "^1.13", diff --git a/legacy/composer.lock b/legacy/composer.lock index c5d34544f..3c2442d63 100644 --- a/legacy/composer.lock +++ b/legacy/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a7659135ed373ebf5f68c9de86891e0b", + "content-hash": "ae64767078beb8eada6d1c6b98f349ad", "packages": [ { "name": "composer/semver", @@ -1020,6 +1020,543 @@ }, "time": "2022-04-21T11:05:19+00:00" }, + { + "name": "symfony/config", + "version": "v5.4.11", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "ec79e03125c1d2477e43dde8528535d90cc78379" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/ec79e03125c1d2477e43dde8528535d90cc78379", + "reference": "ec79e03125c1d2477e43dde8528535d90cc78379", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/filesystem": "^4.4|^5.0|^6.0", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-php80": "^1.16", + "symfony/polyfill-php81": "^1.22" + }, + "conflict": { + "symfony/finder": "<4.4" + }, + "require-dev": { + "symfony/event-dispatcher": "^4.4|^5.0|^6.0", + "symfony/finder": "^4.4|^5.0|^6.0", + "symfony/messenger": "^4.4|^5.0|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/yaml": "^4.4|^5.0|^6.0" + }, + "suggest": { + "symfony/yaml": "To use the yaml reference dumper" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/config/tree/v5.4.11" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-07-20T13:00:38+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v2.5.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66", + "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-01-02T09:53:40+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v5.4.11", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "6699fb0228d1bc35b12aed6dd5e7455457609ddd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/6699fb0228d1bc35b12aed6dd5e7455457609ddd", + "reference": "6699fb0228d1bc35b12aed6dd5e7455457609ddd", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v5.4.11" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-07-20T13:00:38+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.25.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "30885182c981ab175d4d034db0f6f469898070ab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab", + "reference": "30885182c981ab175d4d034db0f6f469898070ab", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-10-20T20:35:02+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace", + "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-10T07:21:04+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/13f6d1271c663dc5ae9fb843a8f16521db7687a1", + "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, { "name": "zf1s/zend-acl", "version": "1.14.0", @@ -4102,88 +4639,6 @@ }, "time": "2016-10-03T07:35:21+00:00" }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.25.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "30885182c981ab175d4d034db0f6f469898070ab" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab", - "reference": "30885182c981ab175d4d034db0f6f469898070ab", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-10-20T20:35:02+00:00" - }, { "name": "symfony/yaml", "version": "v3.4.47",