12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877 |
- <?php
- /*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
- namespace Symfony\Bundle\FrameworkBundle\DependencyInjection;
- use Doctrine\Common\Annotations\Annotation;
- use Doctrine\Common\Cache\Cache;
- use Doctrine\DBAL\Connection;
- use Symfony\Bundle\FullStack;
- use Symfony\Component\Asset\Package;
- use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
- use Symfony\Component\Config\Definition\Builder\NodeBuilder;
- use Symfony\Component\Config\Definition\Builder\TreeBuilder;
- use Symfony\Component\Config\Definition\ConfigurationInterface;
- use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
- use Symfony\Component\DependencyInjection\Exception\LogicException;
- use Symfony\Component\Form\Form;
- use Symfony\Component\HttpClient\HttpClient;
- use Symfony\Component\HttpFoundation\Cookie;
- use Symfony\Component\Lock\Lock;
- use Symfony\Component\Lock\Store\SemaphoreStore;
- use Symfony\Component\Mailer\Mailer;
- use Symfony\Component\Messenger\MessageBusInterface;
- use Symfony\Component\Notifier\Notifier;
- use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface;
- use Symfony\Component\RateLimiter\Policy\TokenBucketLimiter;
- use Symfony\Component\Serializer\Serializer;
- use Symfony\Component\Translation\Translator;
- use Symfony\Component\Validator\Validation;
- use Symfony\Component\WebLink\HttpHeaderSerializer;
- use Symfony\Component\Workflow\WorkflowEvents;
- /**
- * FrameworkExtension configuration structure.
- *
- * @author Jeremy Mikola <jmikola@gmail.com>
- * @author Grégoire Pineau <lyrixx@lyrixx.info>
- */
- class Configuration implements ConfigurationInterface
- {
- private $debug;
- /**
- * @param bool $debug Whether debugging is enabled or not
- */
- public function __construct(bool $debug)
- {
- $this->debug = $debug;
- }
- /**
- * Generates the configuration tree builder.
- *
- * @return TreeBuilder The tree builder
- */
- public function getConfigTreeBuilder()
- {
- $treeBuilder = new TreeBuilder('framework');
- $rootNode = $treeBuilder->getRootNode();
- $rootNode
- ->beforeNormalization()
- ->ifTrue(function ($v) { return !isset($v['assets']) && isset($v['templating']) && class_exists(Package::class); })
- ->then(function ($v) {
- $v['assets'] = [];
- return $v;
- })
- ->end()
- ->children()
- ->scalarNode('secret')->end()
- ->scalarNode('http_method_override')
- ->info("Set true to enable support for the '_method' request parameter to determine the intended HTTP method on POST requests. Note: When using the HttpCache, you need to call the method in your front controller instead")
- ->defaultTrue()
- ->end()
- ->scalarNode('ide')->defaultNull()->end()
- ->booleanNode('test')->end()
- ->scalarNode('default_locale')->defaultValue('en')->end()
- ->arrayNode('trusted_hosts')
- ->beforeNormalization()->ifString()->then(function ($v) { return [$v]; })->end()
- ->prototype('scalar')->end()
- ->end()
- ->scalarNode('trusted_proxies')->end()
- ->arrayNode('trusted_headers')
- ->fixXmlConfig('trusted_header')
- ->performNoDeepMerging()
- ->defaultValue(['x-forwarded-for', 'x-forwarded-port', 'x-forwarded-proto'])
- ->beforeNormalization()->ifString()->then(function ($v) { return $v ? array_map('trim', explode(',', $v)) : []; })->end()
- ->enumPrototype()
- ->values([
- 'forwarded',
- 'x-forwarded-for', 'x-forwarded-host', 'x-forwarded-proto', 'x-forwarded-port', 'x-forwarded-prefix',
- ])
- ->end()
- ->end()
- ->scalarNode('error_controller')
- ->defaultValue('error_controller')
- ->end()
- ->end()
- ;
- $this->addCsrfSection($rootNode);
- $this->addFormSection($rootNode);
- $this->addHttpCacheSection($rootNode);
- $this->addEsiSection($rootNode);
- $this->addSsiSection($rootNode);
- $this->addFragmentsSection($rootNode);
- $this->addProfilerSection($rootNode);
- $this->addWorkflowSection($rootNode);
- $this->addRouterSection($rootNode);
- $this->addSessionSection($rootNode);
- $this->addRequestSection($rootNode);
- $this->addAssetsSection($rootNode);
- $this->addTranslatorSection($rootNode);
- $this->addValidationSection($rootNode);
- $this->addAnnotationsSection($rootNode);
- $this->addSerializerSection($rootNode);
- $this->addPropertyAccessSection($rootNode);
- $this->addPropertyInfoSection($rootNode);
- $this->addCacheSection($rootNode);
- $this->addPhpErrorsSection($rootNode);
- $this->addWebLinkSection($rootNode);
- $this->addLockSection($rootNode);
- $this->addMessengerSection($rootNode);
- $this->addRobotsIndexSection($rootNode);
- $this->addHttpClientSection($rootNode);
- $this->addMailerSection($rootNode);
- $this->addSecretsSection($rootNode);
- $this->addNotifierSection($rootNode);
- $this->addRateLimiterSection($rootNode);
- return $treeBuilder;
- }
- private function addSecretsSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('secrets')
- ->canBeDisabled()
- ->children()
- ->scalarNode('vault_directory')->defaultValue('%kernel.project_dir%/config/secrets/%kernel.runtime_environment%')->cannotBeEmpty()->end()
- ->scalarNode('local_dotenv_file')->defaultValue('%kernel.project_dir%/.env.%kernel.environment%.local')->end()
- ->scalarNode('decryption_env_var')->defaultValue('base64:default::SYMFONY_DECRYPTION_SECRET')->end()
- ->end()
- ->end()
- ->end()
- ;
- }
- private function addCsrfSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('csrf_protection')
- ->treatFalseLike(['enabled' => false])
- ->treatTrueLike(['enabled' => true])
- ->treatNullLike(['enabled' => true])
- ->addDefaultsIfNotSet()
- ->children()
- // defaults to framework.session.enabled && !class_exists(FullStack::class) && interface_exists(CsrfTokenManagerInterface::class)
- ->booleanNode('enabled')->defaultNull()->end()
- ->end()
- ->end()
- ->end()
- ;
- }
- private function addFormSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('form')
- ->info('form configuration')
- ->{!class_exists(FullStack::class) && class_exists(Form::class) ? 'canBeDisabled' : 'canBeEnabled'}()
- ->children()
- ->arrayNode('csrf_protection')
- ->treatFalseLike(['enabled' => false])
- ->treatTrueLike(['enabled' => true])
- ->treatNullLike(['enabled' => true])
- ->addDefaultsIfNotSet()
- ->children()
- ->booleanNode('enabled')->defaultNull()->end() // defaults to framework.csrf_protection.enabled
- ->scalarNode('field_name')->defaultValue('_token')->end()
- ->end()
- ->end()
- // to be set to false in Symfony 6.0
- ->booleanNode('legacy_error_messages')
- ->defaultTrue()
- ->validate()
- ->ifTrue()
- ->then(function ($v) {
- @trigger_error('Since symfony/framework-bundle 5.2: Setting the "framework.form.legacy_error_messages" option to "true" is deprecated. It will have no effect as of Symfony 6.0.', \E_USER_DEPRECATED);
- return $v;
- })
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ;
- }
- private function addHttpCacheSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('http_cache')
- ->info('HTTP cache configuration')
- ->canBeEnabled()
- ->fixXmlConfig('private_header')
- ->children()
- ->booleanNode('debug')->defaultValue('%kernel.debug%')->end()
- ->enumNode('trace_level')
- ->values(['none', 'short', 'full'])
- ->end()
- ->scalarNode('trace_header')->end()
- ->integerNode('default_ttl')->end()
- ->arrayNode('private_headers')
- ->performNoDeepMerging()
- ->scalarPrototype()->end()
- ->end()
- ->booleanNode('allow_reload')->end()
- ->booleanNode('allow_revalidate')->end()
- ->integerNode('stale_while_revalidate')->end()
- ->integerNode('stale_if_error')->end()
- ->end()
- ->end()
- ->end()
- ;
- }
- private function addEsiSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('esi')
- ->info('esi configuration')
- ->canBeEnabled()
- ->end()
- ->end()
- ;
- }
- private function addSsiSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('ssi')
- ->info('ssi configuration')
- ->canBeEnabled()
- ->end()
- ->end();
- }
- private function addFragmentsSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('fragments')
- ->info('fragments configuration')
- ->canBeEnabled()
- ->children()
- ->scalarNode('hinclude_default_template')->defaultNull()->end()
- ->scalarNode('path')->defaultValue('/_fragment')->end()
- ->end()
- ->end()
- ->end()
- ;
- }
- private function addProfilerSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('profiler')
- ->info('profiler configuration')
- ->canBeEnabled()
- ->children()
- ->booleanNode('collect')->defaultTrue()->end()
- ->booleanNode('only_exceptions')->defaultFalse()->end()
- ->booleanNode('only_master_requests')->defaultFalse()->end()
- ->scalarNode('dsn')->defaultValue('file:%kernel.cache_dir%/profiler')->end()
- ->end()
- ->end()
- ->end()
- ;
- }
- private function addWorkflowSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->fixXmlConfig('workflow')
- ->children()
- ->arrayNode('workflows')
- ->canBeEnabled()
- ->beforeNormalization()
- ->always(function ($v) {
- if (\is_array($v) && true === $v['enabled']) {
- $workflows = $v;
- unset($workflows['enabled']);
- if (1 === \count($workflows) && isset($workflows[0]['enabled']) && 1 === \count($workflows[0])) {
- $workflows = [];
- }
- if (1 === \count($workflows) && isset($workflows['workflows']) && array_keys($workflows['workflows']) !== range(0, \count($workflows) - 1) && !empty(array_diff(array_keys($workflows['workflows']), ['audit_trail', 'type', 'marking_store', 'supports', 'support_strategy', 'initial_marking', 'places', 'transitions']))) {
- $workflows = $workflows['workflows'];
- }
- foreach ($workflows as $key => $workflow) {
- if (isset($workflow['enabled']) && false === $workflow['enabled']) {
- throw new LogicException(sprintf('Cannot disable a single workflow. Remove the configuration for the workflow "%s" instead.', $workflow['name']));
- }
- unset($workflows[$key]['enabled']);
- }
- $v = [
- 'enabled' => true,
- 'workflows' => $workflows,
- ];
- }
- return $v;
- })
- ->end()
- ->children()
- ->arrayNode('workflows')
- ->useAttributeAsKey('name')
- ->prototype('array')
- ->fixXmlConfig('support')
- ->fixXmlConfig('place')
- ->fixXmlConfig('transition')
- ->fixXmlConfig('event_to_dispatch', 'events_to_dispatch')
- ->children()
- ->arrayNode('audit_trail')
- ->canBeEnabled()
- ->end()
- ->enumNode('type')
- ->values(['workflow', 'state_machine'])
- ->defaultValue('state_machine')
- ->end()
- ->arrayNode('marking_store')
- ->children()
- ->enumNode('type')
- ->values(['method'])
- ->end()
- ->scalarNode('property')
- ->defaultValue('marking')
- ->end()
- ->scalarNode('service')
- ->cannotBeEmpty()
- ->end()
- ->end()
- ->end()
- ->arrayNode('supports')
- ->beforeNormalization()
- ->ifString()
- ->then(function ($v) { return [$v]; })
- ->end()
- ->prototype('scalar')
- ->cannotBeEmpty()
- ->validate()
- ->ifTrue(function ($v) { return !class_exists($v) && !interface_exists($v, false); })
- ->thenInvalid('The supported class or interface "%s" does not exist.')
- ->end()
- ->end()
- ->end()
- ->scalarNode('support_strategy')
- ->cannotBeEmpty()
- ->end()
- ->arrayNode('initial_marking')
- ->beforeNormalization()->castToArray()->end()
- ->defaultValue([])
- ->prototype('scalar')->end()
- ->end()
- ->variableNode('events_to_dispatch')
- ->defaultValue(null)
- ->validate()
- ->ifTrue(function ($v) {
- if (null === $v) {
- return false;
- }
- if (!\is_array($v)) {
- return true;
- }
- foreach ($v as $value) {
- if (!\is_string($value)) {
- return true;
- }
- if (class_exists(WorkflowEvents::class) && !\in_array($value, WorkflowEvents::ALIASES)) {
- return true;
- }
- }
- return false;
- })
- ->thenInvalid('The value must be "null" or an array of workflow events (like ["workflow.enter"]).')
- ->end()
- ->info('Select which Transition events should be dispatched for this Workflow')
- ->example(['workflow.enter', 'workflow.transition'])
- ->end()
- ->arrayNode('places')
- ->beforeNormalization()
- ->always()
- ->then(function ($places) {
- // It's an indexed array of shape ['place1', 'place2']
- if (isset($places[0]) && \is_string($places[0])) {
- return array_map(function (string $place) {
- return ['name' => $place];
- }, $places);
- }
- // It's an indexed array, we let the validation occur
- if (isset($places[0]) && \is_array($places[0])) {
- return $places;
- }
- foreach ($places as $name => $place) {
- if (\is_array($place) && \array_key_exists('name', $place)) {
- continue;
- }
- $place['name'] = $name;
- $places[$name] = $place;
- }
- return array_values($places);
- })
- ->end()
- ->isRequired()
- ->requiresAtLeastOneElement()
- ->prototype('array')
- ->children()
- ->scalarNode('name')
- ->isRequired()
- ->cannotBeEmpty()
- ->end()
- ->arrayNode('metadata')
- ->normalizeKeys(false)
- ->defaultValue([])
- ->example(['color' => 'blue', 'description' => 'Workflow to manage article.'])
- ->prototype('variable')
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->arrayNode('transitions')
- ->beforeNormalization()
- ->always()
- ->then(function ($transitions) {
- // It's an indexed array, we let the validation occur
- if (isset($transitions[0]) && \is_array($transitions[0])) {
- return $transitions;
- }
- foreach ($transitions as $name => $transition) {
- if (\is_array($transition) && \array_key_exists('name', $transition)) {
- continue;
- }
- $transition['name'] = $name;
- $transitions[$name] = $transition;
- }
- return $transitions;
- })
- ->end()
- ->isRequired()
- ->requiresAtLeastOneElement()
- ->prototype('array')
- ->children()
- ->scalarNode('name')
- ->isRequired()
- ->cannotBeEmpty()
- ->end()
- ->scalarNode('guard')
- ->cannotBeEmpty()
- ->info('An expression to block the transition')
- ->example('is_fully_authenticated() and is_granted(\'ROLE_JOURNALIST\') and subject.getTitle() == \'My first article\'')
- ->end()
- ->arrayNode('from')
- ->beforeNormalization()
- ->ifString()
- ->then(function ($v) { return [$v]; })
- ->end()
- ->requiresAtLeastOneElement()
- ->prototype('scalar')
- ->cannotBeEmpty()
- ->end()
- ->end()
- ->arrayNode('to')
- ->beforeNormalization()
- ->ifString()
- ->then(function ($v) { return [$v]; })
- ->end()
- ->requiresAtLeastOneElement()
- ->prototype('scalar')
- ->cannotBeEmpty()
- ->end()
- ->end()
- ->arrayNode('metadata')
- ->normalizeKeys(false)
- ->defaultValue([])
- ->example(['color' => 'blue', 'description' => 'Workflow to manage article.'])
- ->prototype('variable')
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->arrayNode('metadata')
- ->normalizeKeys(false)
- ->defaultValue([])
- ->example(['color' => 'blue', 'description' => 'Workflow to manage article.'])
- ->prototype('variable')
- ->end()
- ->end()
- ->end()
- ->validate()
- ->ifTrue(function ($v) {
- return $v['supports'] && isset($v['support_strategy']);
- })
- ->thenInvalid('"supports" and "support_strategy" cannot be used together.')
- ->end()
- ->validate()
- ->ifTrue(function ($v) {
- return !$v['supports'] && !isset($v['support_strategy']);
- })
- ->thenInvalid('"supports" or "support_strategy" should be configured.')
- ->end()
- ->beforeNormalization()
- ->always()
- ->then(function ($values) {
- // Special case to deal with XML when the user wants an empty array
- if (\array_key_exists('event_to_dispatch', $values) && null === $values['event_to_dispatch']) {
- $values['events_to_dispatch'] = [];
- unset($values['event_to_dispatch']);
- }
- return $values;
- })
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ;
- }
- private function addRouterSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('router')
- ->info('router configuration')
- ->canBeEnabled()
- ->children()
- ->scalarNode('resource')->isRequired()->end()
- ->scalarNode('type')->end()
- ->scalarNode('default_uri')
- ->info('The default URI used to generate URLs in a non-HTTP context')
- ->defaultNull()
- ->end()
- ->scalarNode('http_port')->defaultValue(80)->end()
- ->scalarNode('https_port')->defaultValue(443)->end()
- ->scalarNode('strict_requirements')
- ->info(
- "set to true to throw an exception when a parameter does not match the requirements\n".
- "set to false to disable exceptions when a parameter does not match the requirements (and return null instead)\n".
- "set to null to disable parameter checks against requirements\n".
- "'true' is the preferred configuration in development mode, while 'false' or 'null' might be preferred in production"
- )
- ->defaultTrue()
- ->end()
- ->booleanNode('utf8')->defaultNull()->end()
- ->end()
- ->end()
- ->end()
- ;
- }
- private function addSessionSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('session')
- ->info('session configuration')
- ->canBeEnabled()
- ->children()
- ->scalarNode('storage_id')->defaultValue('session.storage.native')->end()
- ->scalarNode('handler_id')->defaultValue('session.handler.native_file')->end()
- ->scalarNode('name')
- ->validate()
- ->ifTrue(function ($v) {
- parse_str($v, $parsed);
- return implode('&', array_keys($parsed)) !== (string) $v;
- })
- ->thenInvalid('Session name %s contains illegal character(s)')
- ->end()
- ->end()
- ->scalarNode('cookie_lifetime')->end()
- ->scalarNode('cookie_path')->end()
- ->scalarNode('cookie_domain')->end()
- ->enumNode('cookie_secure')->values([true, false, 'auto'])->end()
- ->booleanNode('cookie_httponly')->defaultTrue()->end()
- ->enumNode('cookie_samesite')->values([null, Cookie::SAMESITE_LAX, Cookie::SAMESITE_STRICT, Cookie::SAMESITE_NONE])->defaultNull()->end()
- ->booleanNode('use_cookies')->end()
- ->scalarNode('gc_divisor')->end()
- ->scalarNode('gc_probability')->defaultValue(1)->end()
- ->scalarNode('gc_maxlifetime')->end()
- ->scalarNode('save_path')->defaultValue('%kernel.cache_dir%/sessions')->end()
- ->integerNode('metadata_update_threshold')
- ->defaultValue(0)
- ->info('seconds to wait between 2 session metadata updates')
- ->end()
- ->integerNode('sid_length')
- ->min(22)
- ->max(256)
- ->end()
- ->integerNode('sid_bits_per_character')
- ->min(4)
- ->max(6)
- ->end()
- ->end()
- ->end()
- ->end()
- ;
- }
- private function addRequestSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('request')
- ->info('request configuration')
- ->canBeEnabled()
- ->fixXmlConfig('format')
- ->children()
- ->arrayNode('formats')
- ->useAttributeAsKey('name')
- ->prototype('array')
- ->beforeNormalization()
- ->ifTrue(function ($v) { return \is_array($v) && isset($v['mime_type']); })
- ->then(function ($v) { return $v['mime_type']; })
- ->end()
- ->beforeNormalization()->castToArray()->end()
- ->prototype('scalar')->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ;
- }
- private function addAssetsSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('assets')
- ->info('assets configuration')
- ->{!class_exists(FullStack::class) && class_exists(Package::class) ? 'canBeDisabled' : 'canBeEnabled'}()
- ->fixXmlConfig('base_url')
- ->children()
- ->scalarNode('version_strategy')->defaultNull()->end()
- ->scalarNode('version')->defaultNull()->end()
- ->scalarNode('version_format')->defaultValue('%%s?%%s')->end()
- ->scalarNode('json_manifest_path')->defaultNull()->end()
- ->scalarNode('base_path')->defaultValue('')->end()
- ->arrayNode('base_urls')
- ->requiresAtLeastOneElement()
- ->beforeNormalization()->castToArray()->end()
- ->prototype('scalar')->end()
- ->end()
- ->end()
- ->validate()
- ->ifTrue(function ($v) {
- return isset($v['version_strategy']) && isset($v['version']);
- })
- ->thenInvalid('You cannot use both "version_strategy" and "version" at the same time under "assets".')
- ->end()
- ->validate()
- ->ifTrue(function ($v) {
- return isset($v['version_strategy']) && isset($v['json_manifest_path']);
- })
- ->thenInvalid('You cannot use both "version_strategy" and "json_manifest_path" at the same time under "assets".')
- ->end()
- ->validate()
- ->ifTrue(function ($v) {
- return isset($v['version']) && isset($v['json_manifest_path']);
- })
- ->thenInvalid('You cannot use both "version" and "json_manifest_path" at the same time under "assets".')
- ->end()
- ->fixXmlConfig('package')
- ->children()
- ->arrayNode('packages')
- ->normalizeKeys(false)
- ->useAttributeAsKey('name')
- ->prototype('array')
- ->fixXmlConfig('base_url')
- ->children()
- ->scalarNode('version_strategy')->defaultNull()->end()
- ->scalarNode('version')
- ->beforeNormalization()
- ->ifTrue(function ($v) { return '' === $v; })
- ->then(function ($v) { return; })
- ->end()
- ->end()
- ->scalarNode('version_format')->defaultNull()->end()
- ->scalarNode('json_manifest_path')->defaultNull()->end()
- ->scalarNode('base_path')->defaultValue('')->end()
- ->arrayNode('base_urls')
- ->requiresAtLeastOneElement()
- ->beforeNormalization()->castToArray()->end()
- ->prototype('scalar')->end()
- ->end()
- ->end()
- ->validate()
- ->ifTrue(function ($v) {
- return isset($v['version_strategy']) && isset($v['version']);
- })
- ->thenInvalid('You cannot use both "version_strategy" and "version" at the same time under "assets" packages.')
- ->end()
- ->validate()
- ->ifTrue(function ($v) {
- return isset($v['version_strategy']) && isset($v['json_manifest_path']);
- })
- ->thenInvalid('You cannot use both "version_strategy" and "json_manifest_path" at the same time under "assets" packages.')
- ->end()
- ->validate()
- ->ifTrue(function ($v) {
- return isset($v['version']) && isset($v['json_manifest_path']);
- })
- ->thenInvalid('You cannot use both "version" and "json_manifest_path" at the same time under "assets" packages.')
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ;
- }
- private function addTranslatorSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('translator')
- ->info('translator configuration')
- ->{!class_exists(FullStack::class) && class_exists(Translator::class) ? 'canBeDisabled' : 'canBeEnabled'}()
- ->fixXmlConfig('fallback')
- ->fixXmlConfig('path')
- ->fixXmlConfig('enabled_locale')
- ->children()
- ->arrayNode('fallbacks')
- ->info('Defaults to the value of "default_locale".')
- ->beforeNormalization()->ifString()->then(function ($v) { return [$v]; })->end()
- ->prototype('scalar')->end()
- ->defaultValue([])
- ->end()
- ->booleanNode('logging')->defaultValue(false)->end()
- ->scalarNode('formatter')->defaultValue('translator.formatter.default')->end()
- ->scalarNode('cache_dir')->defaultValue('%kernel.cache_dir%/translations')->end()
- ->scalarNode('default_path')
- ->info('The default path used to load translations')
- ->defaultValue('%kernel.project_dir%/translations')
- ->end()
- ->arrayNode('paths')
- ->prototype('scalar')->end()
- ->end()
- ->arrayNode('enabled_locales')
- ->prototype('scalar')->end()
- ->defaultValue([])
- ->end()
- ->arrayNode('pseudo_localization')
- ->canBeEnabled()
- ->fixXmlConfig('localizable_html_attribute')
- ->children()
- ->booleanNode('accents')->defaultTrue()->end()
- ->floatNode('expansion_factor')
- ->min(1.0)
- ->defaultValue(1.0)
- ->end()
- ->booleanNode('brackets')->defaultTrue()->end()
- ->booleanNode('parse_html')->defaultFalse()->end()
- ->arrayNode('localizable_html_attributes')
- ->prototype('scalar')->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ;
- }
- private function addValidationSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('validation')
- ->info('validation configuration')
- ->{!class_exists(FullStack::class) && class_exists(Validation::class) ? 'canBeDisabled' : 'canBeEnabled'}()
- ->children()
- ->scalarNode('cache')->end()
- ->booleanNode('enable_annotations')->{!class_exists(FullStack::class) && class_exists(Annotation::class) ? 'defaultTrue' : 'defaultFalse'}()->end()
- ->arrayNode('static_method')
- ->defaultValue(['loadValidatorMetadata'])
- ->prototype('scalar')->end()
- ->treatFalseLike([])
- ->validate()->castToArray()->end()
- ->end()
- ->scalarNode('translation_domain')->defaultValue('validators')->end()
- ->enumNode('email_validation_mode')->values(['html5', 'loose', 'strict'])->end()
- ->arrayNode('mapping')
- ->addDefaultsIfNotSet()
- ->fixXmlConfig('path')
- ->children()
- ->arrayNode('paths')
- ->prototype('scalar')->end()
- ->end()
- ->end()
- ->end()
- ->arrayNode('not_compromised_password')
- ->canBeDisabled()
- ->children()
- ->booleanNode('enabled')
- ->defaultTrue()
- ->info('When disabled, compromised passwords will be accepted as valid.')
- ->end()
- ->scalarNode('endpoint')
- ->defaultNull()
- ->info('API endpoint for the NotCompromisedPassword Validator.')
- ->end()
- ->end()
- ->end()
- ->arrayNode('auto_mapping')
- ->info('A collection of namespaces for which auto-mapping will be enabled by default, or null to opt-in with the EnableAutoMapping constraint.')
- ->example([
- 'App\\Entity\\' => [],
- 'App\\WithSpecificLoaders\\' => ['validator.property_info_loader'],
- ])
- ->useAttributeAsKey('namespace')
- ->normalizeKeys(false)
- ->beforeNormalization()
- ->ifArray()
- ->then(function (array $values): array {
- foreach ($values as $k => $v) {
- if (isset($v['service'])) {
- continue;
- }
- if (isset($v['namespace'])) {
- $values[$k]['services'] = [];
- continue;
- }
- if (!\is_array($v)) {
- $values[$v]['services'] = [];
- unset($values[$k]);
- continue;
- }
- $tmp = $v;
- unset($values[$k]);
- $values[$k]['services'] = $tmp;
- }
- return $values;
- })
- ->end()
- ->arrayPrototype()
- ->fixXmlConfig('service')
- ->children()
- ->arrayNode('services')
- ->prototype('scalar')->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ;
- }
- private function addAnnotationsSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('annotations')
- ->info('annotation configuration')
- ->{class_exists(Annotation::class) ? 'canBeDisabled' : 'canBeEnabled'}()
- ->children()
- ->scalarNode('cache')->defaultValue(interface_exists(Cache::class) ? 'php_array' : 'none')->end()
- ->scalarNode('file_cache_dir')->defaultValue('%kernel.cache_dir%/annotations')->end()
- ->booleanNode('debug')->defaultValue($this->debug)->end()
- ->end()
- ->end()
- ->end()
- ;
- }
- private function addSerializerSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('serializer')
- ->info('serializer configuration')
- ->{!class_exists(FullStack::class) && class_exists(Serializer::class) ? 'canBeDisabled' : 'canBeEnabled'}()
- ->children()
- ->booleanNode('enable_annotations')->{!class_exists(FullStack::class) && class_exists(Annotation::class) ? 'defaultTrue' : 'defaultFalse'}()->end()
- ->scalarNode('name_converter')->end()
- ->scalarNode('circular_reference_handler')->end()
- ->scalarNode('max_depth_handler')->end()
- ->arrayNode('mapping')
- ->addDefaultsIfNotSet()
- ->fixXmlConfig('path')
- ->children()
- ->arrayNode('paths')
- ->prototype('scalar')->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ;
- }
- private function addPropertyAccessSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('property_access')
- ->addDefaultsIfNotSet()
- ->info('Property access configuration')
- ->children()
- ->booleanNode('magic_call')->defaultFalse()->end()
- ->booleanNode('magic_get')->defaultTrue()->end()
- ->booleanNode('magic_set')->defaultTrue()->end()
- ->booleanNode('throw_exception_on_invalid_index')->defaultFalse()->end()
- ->booleanNode('throw_exception_on_invalid_property_path')->defaultTrue()->end()
- ->end()
- ->end()
- ->end()
- ;
- }
- private function addPropertyInfoSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('property_info')
- ->info('Property info configuration')
- ->{!class_exists(FullStack::class) && interface_exists(PropertyInfoExtractorInterface::class) ? 'canBeDisabled' : 'canBeEnabled'}()
- ->end()
- ->end()
- ;
- }
- private function addCacheSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('cache')
- ->info('Cache configuration')
- ->addDefaultsIfNotSet()
- ->fixXmlConfig('pool')
- ->children()
- ->scalarNode('prefix_seed')
- ->info('Used to namespace cache keys when using several apps with the same shared backend')
- ->defaultValue('_%kernel.project_dir%.%kernel.container_class%')
- ->example('my-application-name/%kernel.environment%')
- ->end()
- ->scalarNode('app')
- ->info('App related cache pools configuration')
- ->defaultValue('cache.adapter.filesystem')
- ->end()
- ->scalarNode('system')
- ->info('System related cache pools configuration')
- ->defaultValue('cache.adapter.system')
- ->end()
- ->scalarNode('directory')->defaultValue('%kernel.cache_dir%/pools')->end()
- ->scalarNode('default_doctrine_provider')->end()
- ->scalarNode('default_psr6_provider')->end()
- ->scalarNode('default_redis_provider')->defaultValue('redis://localhost')->end()
- ->scalarNode('default_memcached_provider')->defaultValue('memcached://localhost')->end()
- ->scalarNode('default_pdo_provider')->defaultValue(class_exists(Connection::class) ? 'database_connection' : null)->end()
- ->arrayNode('pools')
- ->useAttributeAsKey('name')
- ->prototype('array')
- ->fixXmlConfig('adapter')
- ->beforeNormalization()
- ->ifTrue(function ($v) { return (isset($v['adapters']) || \is_array($v['adapter'] ?? null)) && isset($v['provider']); })
- ->thenInvalid('Pool cannot have a "provider" while "adapter" is set to a map')
- ->end()
- ->children()
- ->arrayNode('adapters')
- ->performNoDeepMerging()
- ->info('One or more adapters to chain for creating the pool, defaults to "cache.app".')
- ->beforeNormalization()
- ->always()->then(function ($values) {
- if ([0] === array_keys($values) && \is_array($values[0])) {
- return $values[0];
- }
- $adapters = [];
- foreach ($values as $k => $v) {
- if (\is_int($k) && \is_string($v)) {
- $adapters[] = $v;
- } elseif (!\is_array($v)) {
- $adapters[$k] = $v;
- } elseif (isset($v['provider'])) {
- $adapters[$v['provider']] = $v['name'] ?? $v;
- } else {
- $adapters[] = $v['name'] ?? $v;
- }
- }
- return $adapters;
- })
- ->end()
- ->prototype('scalar')->end()
- ->end()
- ->scalarNode('tags')->defaultNull()->end()
- ->booleanNode('public')->defaultFalse()->end()
- ->scalarNode('default_lifetime')
- ->info('Default lifetime of the pool')
- ->example('"600" for 5 minutes expressed in seconds, "PT5M" for five minutes expressed as ISO 8601 time interval, or "5 minutes" as a date expression')
- ->end()
- ->scalarNode('provider')
- ->info('Overwrite the setting from the default provider for this adapter.')
- ->end()
- ->scalarNode('early_expiration_message_bus')
- ->example('"messenger.default_bus" to send early expiration events to the default Messenger bus.')
- ->end()
- ->scalarNode('clearer')->end()
- ->end()
- ->end()
- ->validate()
- ->ifTrue(function ($v) { return isset($v['cache.app']) || isset($v['cache.system']); })
- ->thenInvalid('"cache.app" and "cache.system" are reserved names')
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ;
- }
- private function addPhpErrorsSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('php_errors')
- ->info('PHP errors handling configuration')
- ->addDefaultsIfNotSet()
- ->children()
- ->scalarNode('log')
- ->info('Use the application logger instead of the PHP logger for logging PHP errors.')
- ->example('"true" to use the default configuration: log all errors. "false" to disable. An integer bit field of E_* constants.')
- ->defaultValue($this->debug)
- ->treatNullLike($this->debug)
- ->validate()
- ->ifTrue(function ($v) { return !(\is_int($v) || \is_bool($v)); })
- ->thenInvalid('The "php_errors.log" parameter should be either an integer or a boolean.')
- ->end()
- ->end()
- ->booleanNode('throw')
- ->info('Throw PHP errors as \ErrorException instances.')
- ->defaultValue($this->debug)
- ->treatNullLike($this->debug)
- ->end()
- ->end()
- ->end()
- ->end()
- ;
- }
- private function addLockSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('lock')
- ->info('Lock configuration')
- ->{!class_exists(FullStack::class) && class_exists(Lock::class) ? 'canBeDisabled' : 'canBeEnabled'}()
- ->beforeNormalization()
- ->ifString()->then(function ($v) { return ['enabled' => true, 'resources' => $v]; })
- ->end()
- ->beforeNormalization()
- ->ifTrue(function ($v) { return \is_array($v) && !isset($v['enabled']); })
- ->then(function ($v) { return $v + ['enabled' => true]; })
- ->end()
- ->beforeNormalization()
- ->ifTrue(function ($v) { return \is_array($v) && !isset($v['resources']) && !isset($v['resource']); })
- ->then(function ($v) {
- $e = $v['enabled'];
- unset($v['enabled']);
- return ['enabled' => $e, 'resources' => $v];
- })
- ->end()
- ->addDefaultsIfNotSet()
- ->fixXmlConfig('resource')
- ->children()
- ->arrayNode('resources')
- ->normalizeKeys(false)
- ->useAttributeAsKey('name')
- ->requiresAtLeastOneElement()
- ->defaultValue(['default' => [class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphore' : 'flock']])
- ->beforeNormalization()
- ->ifString()->then(function ($v) { return ['default' => $v]; })
- ->end()
- ->beforeNormalization()
- ->ifTrue(function ($v) { return \is_array($v) && array_keys($v) === range(0, \count($v) - 1); })
- ->then(function ($v) {
- $resources = [];
- foreach ($v as $resource) {
- $resources = array_merge_recursive(
- $resources,
- \is_array($resource) && isset($resource['name'])
- ? [$resource['name'] => $resource['value']]
- : ['default' => $resource]
- );
- }
- return $resources;
- })
- ->end()
- ->prototype('array')
- ->performNoDeepMerging()
- ->beforeNormalization()->ifString()->then(function ($v) { return [$v]; })->end()
- ->prototype('scalar')->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ;
- }
- private function addWebLinkSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('web_link')
- ->info('web links configuration')
- ->{!class_exists(FullStack::class) && class_exists(HttpHeaderSerializer::class) ? 'canBeDisabled' : 'canBeEnabled'}()
- ->end()
- ->end()
- ;
- }
- private function addMessengerSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('messenger')
- ->info('Messenger configuration')
- ->{!class_exists(FullStack::class) && interface_exists(MessageBusInterface::class) ? 'canBeDisabled' : 'canBeEnabled'}()
- ->fixXmlConfig('transport')
- ->fixXmlConfig('bus', 'buses')
- ->validate()
- ->ifTrue(function ($v) { return isset($v['buses']) && \count($v['buses']) > 1 && null === $v['default_bus']; })
- ->thenInvalid('You must specify the "default_bus" if you define more than one bus.')
- ->end()
- ->validate()
- ->ifTrue(static function ($v): bool { return isset($v['buses']) && null !== $v['default_bus'] && !isset($v['buses'][$v['default_bus']]); })
- ->then(static function (array $v): void { throw new InvalidConfigurationException(sprintf('The specified default bus "%s" is not configured. Available buses are "%s".', $v['default_bus'], implode('", "', array_keys($v['buses'])))); })
- ->end()
- ->children()
- ->arrayNode('routing')
- ->normalizeKeys(false)
- ->useAttributeAsKey('message_class')
- ->beforeNormalization()
- ->always()
- ->then(function ($config) {
- if (!\is_array($config)) {
- return [];
- }
- // If XML config with only one routing attribute
- if (2 === \count($config) && isset($config['message-class']) && isset($config['sender'])) {
- $config = [0 => $config];
- }
- $newConfig = [];
- foreach ($config as $k => $v) {
- if (!\is_int($k)) {
- $newConfig[$k] = [
- 'senders' => $v['senders'] ?? (\is_array($v) ? array_values($v) : [$v]),
- ];
- } else {
- $newConfig[$v['message-class']]['senders'] = array_map(
- function ($a) {
- return \is_string($a) ? $a : $a['service'];
- },
- array_values($v['sender'])
- );
- }
- }
- return $newConfig;
- })
- ->end()
- ->prototype('array')
- ->performNoDeepMerging()
- ->children()
- ->arrayNode('senders')
- ->requiresAtLeastOneElement()
- ->prototype('scalar')->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->arrayNode('serializer')
- ->addDefaultsIfNotSet()
- ->children()
- ->scalarNode('default_serializer')
- ->defaultValue('messenger.transport.native_php_serializer')
- ->info('Service id to use as the default serializer for the transports.')
- ->end()
- ->arrayNode('symfony_serializer')
- ->addDefaultsIfNotSet()
- ->children()
- ->scalarNode('format')->defaultValue('json')->info('Serialization format for the messenger.transport.symfony_serializer service (which is not the serializer used by default).')->end()
- ->arrayNode('context')
- ->normalizeKeys(false)
- ->useAttributeAsKey('name')
- ->defaultValue([])
- ->info('Context array for the messenger.transport.symfony_serializer service (which is not the serializer used by default).')
- ->prototype('variable')->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->arrayNode('transports')
- ->normalizeKeys(false)
- ->useAttributeAsKey('name')
- ->arrayPrototype()
- ->beforeNormalization()
- ->ifString()
- ->then(function (string $dsn) {
- return ['dsn' => $dsn];
- })
- ->end()
- ->fixXmlConfig('option')
- ->children()
- ->scalarNode('dsn')->end()
- ->scalarNode('serializer')->defaultNull()->info('Service id of a custom serializer to use.')->end()
- ->arrayNode('options')
- ->normalizeKeys(false)
- ->defaultValue([])
- ->prototype('variable')
- ->end()
- ->end()
- ->arrayNode('retry_strategy')
- ->addDefaultsIfNotSet()
- ->beforeNormalization()
- ->always(function ($v) {
- if (isset($v['service']) && (isset($v['max_retries']) || isset($v['delay']) || isset($v['multiplier']) || isset($v['max_delay']))) {
- throw new \InvalidArgumentException('The "service" cannot be used along with the other "retry_strategy" options.');
- }
- return $v;
- })
- ->end()
- ->children()
- ->scalarNode('service')->defaultNull()->info('Service id to override the retry strategy entirely')->end()
- ->integerNode('max_retries')->defaultValue(3)->min(0)->end()
- ->integerNode('delay')->defaultValue(1000)->min(0)->info('Time in ms to delay (or the initial value when multiplier is used)')->end()
- ->floatNode('multiplier')->defaultValue(2)->min(1)->info('If greater than 1, delay will grow exponentially for each retry: this delay = (delay * (multiple ^ retries))')->end()
- ->integerNode('max_delay')->defaultValue(0)->min(0)->info('Max time in ms that a retry should ever be delayed (0 = infinite)')->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->scalarNode('failure_transport')
- ->defaultNull()
- ->info('Transport name to send failed messages to (after all retries have failed).')
- ->end()
- ->scalarNode('default_bus')->defaultNull()->end()
- ->arrayNode('buses')
- ->defaultValue(['messenger.bus.default' => ['default_middleware' => true, 'middleware' => []]])
- ->normalizeKeys(false)
- ->useAttributeAsKey('name')
- ->arrayPrototype()
- ->addDefaultsIfNotSet()
- ->children()
- ->enumNode('default_middleware')
- ->values([true, false, 'allow_no_handlers'])
- ->defaultTrue()
- ->end()
- ->arrayNode('middleware')
- ->performNoDeepMerging()
- ->beforeNormalization()
- ->ifTrue(function ($v) { return \is_string($v) || (\is_array($v) && !\is_int(key($v))); })
- ->then(function ($v) { return [$v]; })
- ->end()
- ->defaultValue([])
- ->arrayPrototype()
- ->beforeNormalization()
- ->always()
- ->then(function ($middleware): array {
- if (!\is_array($middleware)) {
- return ['id' => $middleware];
- }
- if (isset($middleware['id'])) {
- return $middleware;
- }
- if (1 < \count($middleware)) {
- throw new \InvalidArgumentException('Invalid middleware at path "framework.messenger": a map with a single factory id as key and its arguments as value was expected, '.json_encode($middleware).' given.');
- }
- return [
- 'id' => key($middleware),
- 'arguments' => current($middleware),
- ];
- })
- ->end()
- ->fixXmlConfig('argument')
- ->children()
- ->scalarNode('id')->isRequired()->cannotBeEmpty()->end()
- ->arrayNode('arguments')
- ->normalizeKeys(false)
- ->defaultValue([])
- ->prototype('variable')
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ;
- }
- private function addRobotsIndexSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->booleanNode('disallow_search_engine_index')
- ->info('Enabled by default when debug is enabled.')
- ->defaultValue($this->debug)
- ->treatNullLike($this->debug)
- ->end()
- ->end()
- ;
- }
- private function addHttpClientSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('http_client')
- ->info('HTTP Client configuration')
- ->{!class_exists(FullStack::class) && class_exists(HttpClient::class) ? 'canBeDisabled' : 'canBeEnabled'}()
- ->fixXmlConfig('scoped_client')
- ->beforeNormalization()
- ->always(function ($config) {
- if (empty($config['scoped_clients']) || !\is_array($config['default_options']['retry_failed'] ?? null)) {
- return $config;
- }
- foreach ($config['scoped_clients'] as &$scopedConfig) {
- if (!isset($scopedConfig['retry_failed']) || true === $scopedConfig['retry_failed']) {
- $scopedConfig['retry_failed'] = $config['default_options']['retry_failed'];
- continue;
- }
- if (\is_array($scopedConfig['retry_failed'])) {
- $scopedConfig['retry_failed'] = $scopedConfig['retry_failed'] + $config['default_options']['retry_failed'];
- }
- }
- return $config;
- })
- ->end()
- ->children()
- ->integerNode('max_host_connections')
- ->info('The maximum number of connections to a single host.')
- ->end()
- ->arrayNode('default_options')
- ->fixXmlConfig('header')
- ->children()
- ->arrayNode('headers')
- ->info('Associative array: header => value(s).')
- ->useAttributeAsKey('name')
- ->normalizeKeys(false)
- ->variablePrototype()->end()
- ->end()
- ->integerNode('max_redirects')
- ->info('The maximum number of redirects to follow.')
- ->end()
- ->scalarNode('http_version')
- ->info('The default HTTP version, typically 1.1 or 2.0, leave to null for the best version.')
- ->end()
- ->arrayNode('resolve')
- ->info('Associative array: domain => IP.')
- ->useAttributeAsKey('host')
- ->beforeNormalization()
- ->always(function ($config) {
- if (!\is_array($config)) {
- return [];
- }
- if (!isset($config['host'], $config['value']) || \count($config) > 2) {
- return $config;
- }
- return [$config['host'] => $config['value']];
- })
- ->end()
- ->normalizeKeys(false)
- ->scalarPrototype()->end()
- ->end()
- ->scalarNode('proxy')
- ->info('The URL of the proxy to pass requests through or null for automatic detection.')
- ->end()
- ->scalarNode('no_proxy')
- ->info('A comma separated list of hosts that do not require a proxy to be reached.')
- ->end()
- ->floatNode('timeout')
- ->info('The idle timeout, defaults to the "default_socket_timeout" ini parameter.')
- ->end()
- ->floatNode('max_duration')
- ->info('The maximum execution time for the request+response as a whole.')
- ->end()
- ->scalarNode('bindto')
- ->info('A network interface name, IP address, a host name or a UNIX socket to bind to.')
- ->end()
- ->booleanNode('verify_peer')
- ->info('Indicates if the peer should be verified in an SSL/TLS context.')
- ->end()
- ->booleanNode('verify_host')
- ->info('Indicates if the host should exist as a certificate common name.')
- ->end()
- ->scalarNode('cafile')
- ->info('A certificate authority file.')
- ->end()
- ->scalarNode('capath')
- ->info('A directory that contains multiple certificate authority files.')
- ->end()
- ->scalarNode('local_cert')
- ->info('A PEM formatted certificate file.')
- ->end()
- ->scalarNode('local_pk')
- ->info('A private key file.')
- ->end()
- ->scalarNode('passphrase')
- ->info('The passphrase used to encrypt the "local_pk" file.')
- ->end()
- ->scalarNode('ciphers')
- ->info('A list of SSL/TLS ciphers separated by colons, commas or spaces (e.g. "RC3-SHA:TLS13-AES-128-GCM-SHA256"...)')
- ->end()
- ->arrayNode('peer_fingerprint')
- ->info('Associative array: hashing algorithm => hash(es).')
- ->normalizeKeys(false)
- ->children()
- ->variableNode('sha1')->end()
- ->variableNode('pin-sha256')->end()
- ->variableNode('md5')->end()
- ->end()
- ->end()
- ->append($this->addHttpClientRetrySection())
- ->end()
- ->end()
- ->scalarNode('mock_response_factory')
- ->info('The id of the service that should generate mock responses. It should be either an invokable or an iterable.')
- ->end()
- ->arrayNode('scoped_clients')
- ->useAttributeAsKey('name')
- ->normalizeKeys(false)
- ->arrayPrototype()
- ->fixXmlConfig('header')
- ->beforeNormalization()
- ->always()
- ->then(function ($config) {
- if (!class_exists(HttpClient::class)) {
- throw new LogicException('HttpClient support cannot be enabled as the component is not installed. Try running "composer require symfony/http-client".');
- }
- return \is_array($config) ? $config : ['base_uri' => $config];
- })
- ->end()
- ->validate()
- ->ifTrue(function ($v) { return !isset($v['scope']) && !isset($v['base_uri']); })
- ->thenInvalid('Either "scope" or "base_uri" should be defined.')
- ->end()
- ->validate()
- ->ifTrue(function ($v) { return !empty($v['query']) && !isset($v['base_uri']); })
- ->thenInvalid('"query" applies to "base_uri" but no base URI is defined.')
- ->end()
- ->children()
- ->scalarNode('scope')
- ->info('The regular expression that the request URL must match before adding the other options. When none is provided, the base URI is used instead.')
- ->cannotBeEmpty()
- ->end()
- ->scalarNode('base_uri')
- ->info('The URI to resolve relative URLs, following rules in RFC 3985, section 2.')
- ->cannotBeEmpty()
- ->end()
- ->scalarNode('auth_basic')
- ->info('An HTTP Basic authentication "username:password".')
- ->end()
- ->scalarNode('auth_bearer')
- ->info('A token enabling HTTP Bearer authorization.')
- ->end()
- ->scalarNode('auth_ntlm')
- ->info('A "username:password" pair to use Microsoft NTLM authentication (requires the cURL extension).')
- ->end()
- ->arrayNode('query')
- ->info('Associative array of query string values merged with the base URI.')
- ->useAttributeAsKey('key')
- ->beforeNormalization()
- ->always(function ($config) {
- if (!\is_array($config)) {
- return [];
- }
- if (!isset($config['key'], $config['value']) || \count($config) > 2) {
- return $config;
- }
- return [$config['key'] => $config['value']];
- })
- ->end()
- ->normalizeKeys(false)
- ->scalarPrototype()->end()
- ->end()
- ->arrayNode('headers')
- ->info('Associative array: header => value(s).')
- ->useAttributeAsKey('name')
- ->normalizeKeys(false)
- ->variablePrototype()->end()
- ->end()
- ->integerNode('max_redirects')
- ->info('The maximum number of redirects to follow.')
- ->end()
- ->scalarNode('http_version')
- ->info('The default HTTP version, typically 1.1 or 2.0, leave to null for the best version.')
- ->end()
- ->arrayNode('resolve')
- ->info('Associative array: domain => IP.')
- ->useAttributeAsKey('host')
- ->beforeNormalization()
- ->always(function ($config) {
- if (!\is_array($config)) {
- return [];
- }
- if (!isset($config['host'], $config['value']) || \count($config) > 2) {
- return $config;
- }
- return [$config['host'] => $config['value']];
- })
- ->end()
- ->normalizeKeys(false)
- ->scalarPrototype()->end()
- ->end()
- ->scalarNode('proxy')
- ->info('The URL of the proxy to pass requests through or null for automatic detection.')
- ->end()
- ->scalarNode('no_proxy')
- ->info('A comma separated list of hosts that do not require a proxy to be reached.')
- ->end()
- ->floatNode('timeout')
- ->info('The idle timeout, defaults to the "default_socket_timeout" ini parameter.')
- ->end()
- ->floatNode('max_duration')
- ->info('The maximum execution time for the request+response as a whole.')
- ->end()
- ->scalarNode('bindto')
- ->info('A network interface name, IP address, a host name or a UNIX socket to bind to.')
- ->end()
- ->booleanNode('verify_peer')
- ->info('Indicates if the peer should be verified in an SSL/TLS context.')
- ->end()
- ->booleanNode('verify_host')
- ->info('Indicates if the host should exist as a certificate common name.')
- ->end()
- ->scalarNode('cafile')
- ->info('A certificate authority file.')
- ->end()
- ->scalarNode('capath')
- ->info('A directory that contains multiple certificate authority files.')
- ->end()
- ->scalarNode('local_cert')
- ->info('A PEM formatted certificate file.')
- ->end()
- ->scalarNode('local_pk')
- ->info('A private key file.')
- ->end()
- ->scalarNode('passphrase')
- ->info('The passphrase used to encrypt the "local_pk" file.')
- ->end()
- ->scalarNode('ciphers')
- ->info('A list of SSL/TLS ciphers separated by colons, commas or spaces (e.g. "RC3-SHA:TLS13-AES-128-GCM-SHA256"...)')
- ->end()
- ->arrayNode('peer_fingerprint')
- ->info('Associative array: hashing algorithm => hash(es).')
- ->normalizeKeys(false)
- ->children()
- ->variableNode('sha1')->end()
- ->variableNode('pin-sha256')->end()
- ->variableNode('md5')->end()
- ->end()
- ->end()
- ->append($this->addHttpClientRetrySection())
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ;
- }
- private function addHttpClientRetrySection()
- {
- $root = new NodeBuilder();
- return $root
- ->arrayNode('retry_failed')
- ->fixXmlConfig('http_code')
- ->canBeEnabled()
- ->addDefaultsIfNotSet()
- ->beforeNormalization()
- ->always(function ($v) {
- if (isset($v['retry_strategy']) && (isset($v['http_codes']) || isset($v['delay']) || isset($v['multiplier']) || isset($v['max_delay']) || isset($v['jitter']))) {
- throw new \InvalidArgumentException('The "retry_strategy" option cannot be used along with the "http_codes", "delay", "multiplier", "max_delay" or "jitter" options.');
- }
- return $v;
- })
- ->end()
- ->children()
- ->scalarNode('retry_strategy')->defaultNull()->info('service id to override the retry strategy')->end()
- ->arrayNode('http_codes')
- ->performNoDeepMerging()
- ->beforeNormalization()
- ->ifArray()
- ->then(static function ($v) {
- $list = [];
- foreach ($v as $key => $val) {
- if (is_numeric($val)) {
- $list[] = ['code' => $val];
- } elseif (\is_array($val)) {
- if (isset($val['code']) || isset($val['methods'])) {
- $list[] = $val;
- } else {
- $list[] = ['code' => $key, 'methods' => $val];
- }
- } elseif (true === $val || null === $val) {
- $list[] = ['code' => $key];
- }
- }
- return $list;
- })
- ->end()
- ->useAttributeAsKey('code')
- ->arrayPrototype()
- ->fixXmlConfig('method')
- ->children()
- ->integerNode('code')->end()
- ->arrayNode('methods')
- ->beforeNormalization()
- ->ifArray()
- ->then(function ($v) {
- return array_map('strtoupper', $v);
- })
- ->end()
- ->prototype('scalar')->end()
- ->info('A list of HTTP methods that triggers a retry for this status code. When empty, all methods are retried')
- ->end()
- ->end()
- ->end()
- ->info('A list of HTTP status code that triggers a retry')
- ->end()
- ->integerNode('max_retries')->defaultValue(3)->min(0)->end()
- ->integerNode('delay')->defaultValue(1000)->min(0)->info('Time in ms to delay (or the initial value when multiplier is used)')->end()
- ->floatNode('multiplier')->defaultValue(2)->min(1)->info('If greater than 1, delay will grow exponentially for each retry: delay * (multiple ^ retries)')->end()
- ->integerNode('max_delay')->defaultValue(0)->min(0)->info('Max time in ms that a retry should ever be delayed (0 = infinite)')->end()
- ->floatNode('jitter')->defaultValue(0.1)->min(0)->max(1)->info('Randomness in percent (between 0 and 1) to apply to the delay')->end()
- ->end()
- ;
- }
- private function addMailerSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('mailer')
- ->info('Mailer configuration')
- ->{!class_exists(FullStack::class) && class_exists(Mailer::class) ? 'canBeDisabled' : 'canBeEnabled'}()
- ->validate()
- ->ifTrue(function ($v) { return isset($v['dsn']) && \count($v['transports']); })
- ->thenInvalid('"dsn" and "transports" cannot be used together.')
- ->end()
- ->fixXmlConfig('transport')
- ->fixXmlConfig('header')
- ->children()
- ->scalarNode('message_bus')->defaultNull()->info('The message bus to use. Defaults to the default bus if the Messenger component is installed.')->end()
- ->scalarNode('dsn')->defaultNull()->end()
- ->arrayNode('transports')
- ->useAttributeAsKey('name')
- ->prototype('scalar')->end()
- ->end()
- ->arrayNode('envelope')
- ->info('Mailer Envelope configuration')
- ->children()
- ->scalarNode('sender')->end()
- ->arrayNode('recipients')
- ->performNoDeepMerging()
- ->beforeNormalization()
- ->ifArray()
- ->then(function ($v) {
- return array_filter(array_values($v));
- })
- ->end()
- ->prototype('scalar')->end()
- ->end()
- ->end()
- ->end()
- ->arrayNode('headers')
- ->normalizeKeys(false)
- ->useAttributeAsKey('name')
- ->prototype('array')
- ->normalizeKeys(false)
- ->beforeNormalization()
- ->ifTrue(function ($v) { return !\is_array($v) || array_keys($v) !== ['value']; })
- ->then(function ($v) { return ['value' => $v]; })
- ->end()
- ->children()
- ->variableNode('value')->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ;
- }
- private function addNotifierSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('notifier')
- ->info('Notifier configuration')
- ->{!class_exists(FullStack::class) && class_exists(Notifier::class) ? 'canBeDisabled' : 'canBeEnabled'}()
- ->fixXmlConfig('chatter_transport')
- ->children()
- ->arrayNode('chatter_transports')
- ->useAttributeAsKey('name')
- ->prototype('scalar')->end()
- ->end()
- ->end()
- ->fixXmlConfig('texter_transport')
- ->children()
- ->arrayNode('texter_transports')
- ->useAttributeAsKey('name')
- ->prototype('scalar')->end()
- ->end()
- ->end()
- ->children()
- ->booleanNode('notification_on_failed_messages')->defaultFalse()->end()
- ->end()
- ->children()
- ->arrayNode('channel_policy')
- ->useAttributeAsKey('name')
- ->prototype('array')
- ->beforeNormalization()->ifString()->then(function (string $v) { return [$v]; })->end()
- ->prototype('scalar')->end()
- ->end()
- ->end()
- ->end()
- ->fixXmlConfig('admin_recipient')
- ->children()
- ->arrayNode('admin_recipients')
- ->prototype('array')
- ->children()
- ->scalarNode('email')->cannotBeEmpty()->end()
- ->scalarNode('phone')->defaultValue('')->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ;
- }
- private function addRateLimiterSection(ArrayNodeDefinition $rootNode)
- {
- $rootNode
- ->children()
- ->arrayNode('rate_limiter')
- ->info('Rate limiter configuration')
- ->{!class_exists(FullStack::class) && class_exists(TokenBucketLimiter::class) ? 'canBeDisabled' : 'canBeEnabled'}()
- ->fixXmlConfig('limiter')
- ->beforeNormalization()
- ->ifTrue(function ($v) { return \is_array($v) && !isset($v['limiters']) && !isset($v['limiter']); })
- ->then(function (array $v) {
- $newV = [
- 'enabled' => $v['enabled'] ?? true,
- ];
- unset($v['enabled']);
- $newV['limiters'] = $v;
- return $newV;
- })
- ->end()
- ->children()
- ->arrayNode('limiters')
- ->useAttributeAsKey('name')
- ->arrayPrototype()
- ->children()
- ->scalarNode('lock_factory')
- ->info('The service ID of the lock factory used by this limiter')
- ->defaultValue('lock.factory')
- ->end()
- ->scalarNode('cache_pool')
- ->info('The cache pool to use for storing the current limiter state')
- ->defaultValue('cache.rate_limiter')
- ->end()
- ->scalarNode('storage_service')
- ->info('The service ID of a custom storage implementation, this precedes any configured "cache_pool"')
- ->defaultNull()
- ->end()
- ->enumNode('policy')
- ->info('The algorithm to be used by this limiter')
- ->isRequired()
- ->values(['fixed_window', 'token_bucket', 'sliding_window', 'no_limit'])
- ->end()
- ->integerNode('limit')
- ->info('The maximum allowed hits in a fixed interval or burst')
- ->isRequired()
- ->end()
- ->scalarNode('interval')
- ->info('Configures the fixed interval if "policy" is set to "fixed_window" or "sliding_window". The value must be a number followed by "second", "minute", "hour", "day", "week" or "month" (or their plural equivalent).')
- ->end()
- ->arrayNode('rate')
- ->info('Configures the fill rate if "policy" is set to "token_bucket"')
- ->children()
- ->scalarNode('interval')
- ->info('Configures the rate interval. The value must be a number followed by "second", "minute", "hour", "day", "week" or "month" (or their plural equivalent).')
- ->end()
- ->integerNode('amount')->info('Amount of tokens to add each interval')->defaultValue(1)->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
- ;
- }
- }
|