N°8772 - Form dependencies manager implementation

- Form SDK implementation
- Basic Forms
- Dynamics Forms
- Basic Blocks + Data Model Block
- Form Compilation
- Turbo integration
This commit is contained in:
Benjamin Dalsass
2025-12-30 11:42:55 +01:00
committed by GitHub
parent 3955b4eb22
commit 4c1ad0f4f2
813 changed files with 115243 additions and 489 deletions

View File

@@ -244,6 +244,10 @@ return array(
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Toolbar\\ToolbarSpacer\\ToolbarSpacer' => $baseDir . '/sources/Application/UI/Base/Component/Toolbar/ToolbarSpacer/ToolbarSpacer.php',
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Toolbar\\ToolbarSpacer\\ToolbarSpacerUIBlockFactory' => $baseDir . '/sources/Application/UI/Base/Component/Toolbar/ToolbarSpacer/ToolbarSpacerUIBlockFactory.php',
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Toolbar\\ToolbarUIBlockFactory' => $baseDir . '/sources/Application/UI/Base/Component/Toolbar/ToolbarUIBlockFactory.php',
'Combodo\\iTop\\Application\\UI\\Base\\Component\\TurboForm\\TurboForm' => $baseDir . '/sources/Application/UI/Base/Component/TurboForm/TurboForm.php',
'Combodo\\iTop\\Application\\UI\\Base\\Component\\TurboForm\\TurboFormUIBlockFactory' => $baseDir . '/sources/Application/UI/Base/Component/TurboForm/TurboFormUIBlockFactory.php',
'Combodo\\iTop\\Application\\UI\\Base\\Component\\TurboUpdate\\TurboStream' => $baseDir . '/sources/Application/UI/Base/Component/TurboStream/TurboStream.php',
'Combodo\\iTop\\Application\\UI\\Base\\Component\\TurboUpdate\\TurboStreamUIBlockFactory' => $baseDir . '/sources/Application/UI/Base/Component/TurboStream/TurboStreamUIBlockFactory.php',
'Combodo\\iTop\\Application\\UI\\Base\\Layout\\ActivityPanel\\ActivityEntry\\ActivityEntry' => $baseDir . '/sources/Application/UI/Base/Layout/ActivityPanel/ActivityEntry/ActivityEntry.php',
'Combodo\\iTop\\Application\\UI\\Base\\Layout\\ActivityPanel\\ActivityEntry\\ActivityEntryFactory' => $baseDir . '/sources/Application/UI/Base/Layout/ActivityPanel/ActivityEntry/ActivityEntryFactory.php',
'Combodo\\iTop\\Application\\UI\\Base\\Layout\\ActivityPanel\\ActivityEntry\\CMDBChangeOp\\CMDBChangeOpAttachmentAddedFactory' => $baseDir . '/sources/Application/UI/Base/Layout/ActivityPanel/ActivityEntry/CMDBChangeOp/CMDBChangeOpAttachmentAddedFactory.php',
@@ -341,6 +345,7 @@ return array(
'Combodo\\iTop\\Controller\\AjaxRenderController' => $baseDir . '/sources/Controller/AjaxRenderController.php',
'Combodo\\iTop\\Controller\\Base\\Layout\\ActivityPanelController' => $baseDir . '/sources/Controller/Base/Layout/ActivityPanelController.php',
'Combodo\\iTop\\Controller\\Base\\Layout\\ObjectController' => $baseDir . '/sources/Controller/Base/Layout/ObjectController.php',
'Combodo\\iTop\\Controller\\Base\\Layout\\OqlController' => $baseDir . '/sources/Controller/Base/Layout/OqlController.php',
'Combodo\\iTop\\Controller\\Links\\LinkSetController' => $baseDir . '/sources/Controller/Links/LinkSetController.php',
'Combodo\\iTop\\Controller\\Newsroom\\iTopNewsroomController' => $baseDir . '/sources/Controller/Newsroom/iTopNewsroomController.php',
'Combodo\\iTop\\Controller\\Notifications\\ActionController' => $baseDir . '/sources/Controller/Notifications/ActionController.php',
@@ -473,8 +478,103 @@ return array(
'Combodo\\iTop\\Form\\Validator\\MultipleChoicesValidator' => $baseDir . '/sources/Form/Validator/MultipleChoicesValidator.php',
'Combodo\\iTop\\Form\\Validator\\NotEmptyExtKeyValidator' => $baseDir . '/sources/Form/Validator/NotEmptyExtKeyValidator.php',
'Combodo\\iTop\\Form\\Validator\\SelectObjectValidator' => $baseDir . '/sources/Form/Validator/SelectObjectValidator.php',
'Combodo\\iTop\\Forms\\Block\\AbstractFormBlock' => $baseDir . '/sources/Forms/Block/AbstractFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\AbstractTypeFormBlock' => $baseDir . '/sources/Forms/Block/AbstractTypeFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\CheckboxFormBlock' => $baseDir . '/sources/Forms/Block/Base/CheckboxFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\ChoiceFormBlock' => $baseDir . '/sources/Forms/Block/Base/ChoiceFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\ChoiceFromInputsBlock' => $baseDir . '/sources/Forms/Block/Base/ChoiceFromInputsBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\CollectionBlock' => $baseDir . '/sources/Forms/Block/Base/CollectionBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\DateFormBlock' => $baseDir . '/sources/Forms/Block/Base/DateFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\DateTimeFormBlock' => $baseDir . '/sources/Forms/Block/Base/DateTimeFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\FormBlock' => $baseDir . '/sources/Forms/Block/Base/FormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\HiddenFormBlock' => $baseDir . '/sources/Forms/Block/Base/HiddenFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\IntegerFormBlock' => $baseDir . '/sources/Forms/Block/Base/IntegerFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\NumberFormBlock' => $baseDir . '/sources/Forms/Block/Base/NumberFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\TextAreaFormBlock' => $baseDir . '/sources/Forms/Block/Base/TextAreaFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\TextFormBlock' => $baseDir . '/sources/Forms/Block/Base/TextFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\DataModel\\AttributeChoiceFormBlock' => $baseDir . '/sources/Forms/Block/DataModel/AttributeChoiceFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\DataModel\\AttributeTypeChoiceFormBlock' => $baseDir . '/sources/Forms/Block/DataModel/AttributeTypeChoiceFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\DataModel\\AttributeValueChoiceFormBlock' => $baseDir . '/sources/Forms/Block/DataModel/AttributeValueChoiceFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\DataModel\\Dashlet\\AggregateFunctionFormBlock' => $baseDir . '/sources/Forms/Block/DataModel/Dashlet/AggregateFunctionFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\DataModel\\Dashlet\\ClassAttributeGroupByFormBlock' => $baseDir . '/sources/Forms/Block/DataModel/Dashlet/ClassAttributeGroupByFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\DataModel\\LabelFormBlock' => $baseDir . '/sources/Forms/Block/DataModel/LabelFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\DataModel\\OqlFormBlock' => $baseDir . '/sources/Forms/Block/DataModel/OqlFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Expression\\AbstractExpressionFormBlock' => $baseDir . '/sources/Forms/Block/Expression/AbstractExpressionFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Expression\\BooleanExpressionFormBlock' => $baseDir . '/sources/Forms/Block/Expression/BooleanExpressionFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Expression\\NumberExpressionFormBlock' => $baseDir . '/sources/Forms/Block/Expression/NumberExpressionFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Expression\\StringExpressionFormBlock' => $baseDir . '/sources/Forms/Block/Expression/StringExpressionFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\FormBlockException' => $baseDir . '/sources/Forms/Block/FormBlockException.php',
'Combodo\\iTop\\Forms\\Block\\FormBlockHelper' => $baseDir . '/sources/Forms/Block/FormBlockHelper.php',
'Combodo\\iTop\\Forms\\Block\\FormBlockService' => $baseDir . '/sources/Forms/Block/FormBlockService.php',
'Combodo\\iTop\\Forms\\Block\\IFormBlock' => $baseDir . '/sources/Forms/Block/IFormBlock.php',
'Combodo\\iTop\\Forms\\Compiler\\FormsCompiler' => $baseDir . '/sources/Forms/Compiler/FormsCompiler.php',
'Combodo\\iTop\\Forms\\Compiler\\FormsCompilerException' => $baseDir . '/sources/Forms/Compiler/FormsCompilerException.php',
'Combodo\\iTop\\Forms\\Controller\\FormsController' => $baseDir . '/sources/Forms/Controller/FormsController.php',
'Combodo\\iTop\\Forms\\FormBuilder\\DependencyHandler' => $baseDir . '/sources/Forms/FormBuilder/DependencyHandler.php',
'Combodo\\iTop\\Forms\\FormBuilder\\DependencyMap' => $baseDir . '/sources/Forms/FormBuilder/DependencyMap.php',
'Combodo\\iTop\\Forms\\FormBuilder\\FormBuilder' => $baseDir . '/sources/Forms/FormBuilder/FormBuilder.php',
'Combodo\\iTop\\Forms\\FormBuilder\\FormBuilderException' => $baseDir . '/sources/Forms/FormBuilder/FormBuilderException.php',
'Combodo\\iTop\\Forms\\FormBuilder\\FormHelper' => $baseDir . '/sources/Forms/FormBuilder/FormHelper.php',
'Combodo\\iTop\\Forms\\FormBuilder\\FormTypeExtension' => $baseDir . '/sources/Forms/FormBuilder/FormTypeExtension.php',
'Combodo\\iTop\\Forms\\FormBuilder\\ResolvedFormType' => $baseDir . '/sources/Forms/FormBuilder/ResolvedFormType.php',
'Combodo\\iTop\\Forms\\FormBuilder\\ResolvedFormTypeFactory' => $baseDir . '/sources/Forms/FormBuilder/ResolvedFormTypeFactory.php',
'Combodo\\iTop\\Forms\\FormType\\Base\\ChoiceFormType' => $baseDir . '/sources/Forms/FormType/Base/ChoiceFormType.php',
'Combodo\\iTop\\Forms\\FormType\\Base\\CollectionFormType' => $baseDir . '/sources/Forms/FormType/Base/CollectionFormType.php',
'Combodo\\iTop\\Forms\\FormType\\Base\\FormType' => $baseDir . '/sources/Forms/FormType/Base/FormType.php',
'Combodo\\iTop\\Forms\\FormType\\DataModel\\OqlFormType' => $baseDir . '/sources/Forms/FormType/DataModel/OqlFormType.php',
'Combodo\\iTop\\Forms\\FormType\\FormTypeHelper' => $baseDir . '/sources/Forms/FormType/FormTypeHelper.php',
'Combodo\\iTop\\Forms\\Forms' => $baseDir . '/sources/Forms/Forms.php',
'Combodo\\iTop\\Forms\\FormsException' => $baseDir . '/sources/Forms/FormsException.php',
'Combodo\\iTop\\Forms\\IO\\AbstractFormIO' => $baseDir . '/sources/Forms/IO/AbstractFormIO.php',
'Combodo\\iTop\\Forms\\IO\\Converter\\AbstractConverter' => $baseDir . '/sources/Forms/IO/Converter/AbstractConverter.php',
'Combodo\\iTop\\Forms\\IO\\Converter\\ChoiceValueToLabelConverter' => $baseDir . '/sources/Forms/IO/Converter/ChoiceValueToLabelConverter.php',
'Combodo\\iTop\\Forms\\IO\\Converter\\CollectionToCountConverter' => $baseDir . '/sources/Forms/IO/Converter/CollectionToCountConverter.php',
'Combodo\\iTop\\Forms\\IO\\Converter\\OqlToClassConverter' => $baseDir . '/sources/Forms/IO/Converter/OqlToClassConverter.php',
'Combodo\\iTop\\Forms\\IO\\FormBinding' => $baseDir . '/sources/Forms/IO/FormBinding.php',
'Combodo\\iTop\\Forms\\IO\\FormBlockIOException' => $baseDir . '/sources/Forms/IO/FormBlockIOException.php',
'Combodo\\iTop\\Forms\\IO\\FormInput' => $baseDir . '/sources/Forms/IO/FormInput.php',
'Combodo\\iTop\\Forms\\IO\\FormOutput' => $baseDir . '/sources/Forms/IO/FormOutput.php',
'Combodo\\iTop\\Forms\\IO\\Format\\AbstractIOFormat' => $baseDir . '/sources/Forms/IO/Format/AbstractIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\AttributeIOFormat' => $baseDir . '/sources/Forms/IO/Format/AttributeIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\AttributeTypeArrayIOFormat' => $baseDir . '/sources/Forms/IO/Format/AttributeTypeArrayIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\AttributeTypeIOFormat' => $baseDir . '/sources/Forms/IO/Format/AttributeTypeIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\BooleanIOFormat' => $baseDir . '/sources/Forms/IO/Format/BooleanIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\ClassIOFormat' => $baseDir . '/sources/Forms/IO/Format/ClassIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\IntegerIOFormat' => $baseDir . '/sources/Forms/IO/Format/IntegerIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\NumberIOFormat' => $baseDir . '/sources/Forms/IO/Format/NumberIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\StringIOFormat' => $baseDir . '/sources/Forms/IO/Format/StringIOFormat.php',
'Combodo\\iTop\\Forms\\Register\\IORegister' => $baseDir . '/sources/Forms/Register/IORegister.php',
'Combodo\\iTop\\Forms\\Register\\Option' => $baseDir . '/sources/Forms/Register/Option.php',
'Combodo\\iTop\\Forms\\Register\\OptionsRegister' => $baseDir . '/sources/Forms/Register/OptionsRegister.php',
'Combodo\\iTop\\Forms\\Register\\RegisterException' => $baseDir . '/sources/Forms/Register/RegisterException.php',
'Combodo\\iTop\\Forms\\Twig\\Extension\\FormCompatibilityExtension' => $baseDir . '/sources/Forms/Twig/Extension/FormCompatibilityExtension.php',
'Combodo\\iTop\\Forms\\Validator\\AttributeExist' => $baseDir . '/sources/Forms/Validator/AttributeExist.php',
'Combodo\\iTop\\Forms\\Validator\\AttributeExistValidator' => $baseDir . '/sources/Forms/Validator/AttributeExistValidator.php',
'Combodo\\iTop\\PhpParser\\Evaluation\\PhpExpressionEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/PhpExpressionEvaluator.php',
'Combodo\\iTop\\PropertyTree\\AbstractProperty' => $baseDir . '/sources/PropertyTree/AbstractProperty.php',
'Combodo\\iTop\\PropertyTree\\Property' => $baseDir . '/sources/PropertyTree/Property.php',
'Combodo\\iTop\\PropertyTree\\PropertyTree' => $baseDir . '/sources/PropertyTree/PropertyTree.php',
'Combodo\\iTop\\PropertyTree\\PropertyTreeDesign' => $baseDir . '/sources/PropertyTree/PropertyTreeDesign.php',
'Combodo\\iTop\\PropertyTree\\PropertyTreeException' => $baseDir . '/sources/PropertyTree/PropertyTreeException.php',
'Combodo\\iTop\\PropertyTree\\PropertyTreeFactory' => $baseDir . '/sources/PropertyTree/PropertyTreeFactory.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\AbstractValueType' => $baseDir . '/sources/PropertyTree/ValueType/AbstractValueType.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeAggregateFunction' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeAggregateFunction.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeBoolean' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeBoolean.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeChoice' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeChoice.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeChoiceFromInput' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeChoiceFromInput.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClass' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeClass.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClassAttribute' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeClassAttribute.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClassAttributeGroupBy' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeClassAttributeGroupBy.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClassAttributeValue' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeClassAttributeValue.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeCollection' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeCollection.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeFactory' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeFactory.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeIcon' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeIcon.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeInteger' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeInteger.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeLabel' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeLabel.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeOQL' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeOQL.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeProfileName' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeProfileName.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeString' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeString.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeText' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeText.php',
'Combodo\\iTop\\Renderer\\BlockRenderer' => $baseDir . '/sources/Renderer/BlockRenderer.php',
'Combodo\\iTop\\Renderer\\Bootstrap\\BsFieldRendererMappings' => $baseDir . '/sources/Renderer/Bootstrap/BsFieldRendererMappings.php',
'Combodo\\iTop\\Renderer\\Bootstrap\\BsFormRenderer' => $baseDir . '/sources/Renderer/Bootstrap/BsFormRenderer.php',
@@ -497,6 +597,8 @@ return array(
'Combodo\\iTop\\Service\\Base\\ObjectRepository' => $baseDir . '/sources/Service/Base/ObjectRepository.php',
'Combodo\\iTop\\Service\\Base\\iDataPostProcessor' => $baseDir . '/sources/Service/Base/iDataPostProcessor.php',
'Combodo\\iTop\\Service\\Cache\\DataModelDependantCache' => $baseDir . '/sources/Service/Cache/DataModelDependantCache.php',
'Combodo\\iTop\\Service\\DependencyInjection\\DIException' => $baseDir . '/sources/Service/DependencyInjection/DIException.php',
'Combodo\\iTop\\Service\\DependencyInjection\\DIService' => $baseDir . '/sources/Service/DependencyInjection/DIService.php',
'Combodo\\iTop\\Service\\Events\\Description\\EventDataDescription' => $baseDir . '/sources/Service/Events/Description/EventDataDescription.php',
'Combodo\\iTop\\Service\\Events\\Description\\EventDescription' => $baseDir . '/sources/Service/Events/Description/EventDescription.php',
'Combodo\\iTop\\Service\\Events\\EventData' => $baseDir . '/sources/Service/Events/EventData.php',
@@ -3071,6 +3173,227 @@ return array(
'Symfony\\Component\\String\\Slugger\\AsciiSlugger' => $vendorDir . '/symfony/string/Slugger/AsciiSlugger.php',
'Symfony\\Component\\String\\Slugger\\SluggerInterface' => $vendorDir . '/symfony/string/Slugger/SluggerInterface.php',
'Symfony\\Component\\String\\UnicodeString' => $vendorDir . '/symfony/string/UnicodeString.php',
'Symfony\\Component\\Validator\\Attribute\\HasNamedArguments' => $vendorDir . '/symfony/validator/Attribute/HasNamedArguments.php',
'Symfony\\Component\\Validator\\Command\\DebugCommand' => $vendorDir . '/symfony/validator/Command/DebugCommand.php',
'Symfony\\Component\\Validator\\Constraint' => $vendorDir . '/symfony/validator/Constraint.php',
'Symfony\\Component\\Validator\\ConstraintValidator' => $vendorDir . '/symfony/validator/ConstraintValidator.php',
'Symfony\\Component\\Validator\\ConstraintValidatorFactory' => $vendorDir . '/symfony/validator/ConstraintValidatorFactory.php',
'Symfony\\Component\\Validator\\ConstraintValidatorFactoryInterface' => $vendorDir . '/symfony/validator/ConstraintValidatorFactoryInterface.php',
'Symfony\\Component\\Validator\\ConstraintValidatorInterface' => $vendorDir . '/symfony/validator/ConstraintValidatorInterface.php',
'Symfony\\Component\\Validator\\ConstraintViolation' => $vendorDir . '/symfony/validator/ConstraintViolation.php',
'Symfony\\Component\\Validator\\ConstraintViolationInterface' => $vendorDir . '/symfony/validator/ConstraintViolationInterface.php',
'Symfony\\Component\\Validator\\ConstraintViolationList' => $vendorDir . '/symfony/validator/ConstraintViolationList.php',
'Symfony\\Component\\Validator\\ConstraintViolationListInterface' => $vendorDir . '/symfony/validator/ConstraintViolationListInterface.php',
'Symfony\\Component\\Validator\\Constraints\\AbstractComparison' => $vendorDir . '/symfony/validator/Constraints/AbstractComparison.php',
'Symfony\\Component\\Validator\\Constraints\\AbstractComparisonValidator' => $vendorDir . '/symfony/validator/Constraints/AbstractComparisonValidator.php',
'Symfony\\Component\\Validator\\Constraints\\All' => $vendorDir . '/symfony/validator/Constraints/All.php',
'Symfony\\Component\\Validator\\Constraints\\AllValidator' => $vendorDir . '/symfony/validator/Constraints/AllValidator.php',
'Symfony\\Component\\Validator\\Constraints\\AtLeastOneOf' => $vendorDir . '/symfony/validator/Constraints/AtLeastOneOf.php',
'Symfony\\Component\\Validator\\Constraints\\AtLeastOneOfValidator' => $vendorDir . '/symfony/validator/Constraints/AtLeastOneOfValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Bic' => $vendorDir . '/symfony/validator/Constraints/Bic.php',
'Symfony\\Component\\Validator\\Constraints\\BicValidator' => $vendorDir . '/symfony/validator/Constraints/BicValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Blank' => $vendorDir . '/symfony/validator/Constraints/Blank.php',
'Symfony\\Component\\Validator\\Constraints\\BlankValidator' => $vendorDir . '/symfony/validator/Constraints/BlankValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Callback' => $vendorDir . '/symfony/validator/Constraints/Callback.php',
'Symfony\\Component\\Validator\\Constraints\\CallbackValidator' => $vendorDir . '/symfony/validator/Constraints/CallbackValidator.php',
'Symfony\\Component\\Validator\\Constraints\\CardScheme' => $vendorDir . '/symfony/validator/Constraints/CardScheme.php',
'Symfony\\Component\\Validator\\Constraints\\CardSchemeValidator' => $vendorDir . '/symfony/validator/Constraints/CardSchemeValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Cascade' => $vendorDir . '/symfony/validator/Constraints/Cascade.php',
'Symfony\\Component\\Validator\\Constraints\\Choice' => $vendorDir . '/symfony/validator/Constraints/Choice.php',
'Symfony\\Component\\Validator\\Constraints\\ChoiceValidator' => $vendorDir . '/symfony/validator/Constraints/ChoiceValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Cidr' => $vendorDir . '/symfony/validator/Constraints/Cidr.php',
'Symfony\\Component\\Validator\\Constraints\\CidrValidator' => $vendorDir . '/symfony/validator/Constraints/CidrValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Collection' => $vendorDir . '/symfony/validator/Constraints/Collection.php',
'Symfony\\Component\\Validator\\Constraints\\CollectionValidator' => $vendorDir . '/symfony/validator/Constraints/CollectionValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Composite' => $vendorDir . '/symfony/validator/Constraints/Composite.php',
'Symfony\\Component\\Validator\\Constraints\\Compound' => $vendorDir . '/symfony/validator/Constraints/Compound.php',
'Symfony\\Component\\Validator\\Constraints\\CompoundValidator' => $vendorDir . '/symfony/validator/Constraints/CompoundValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Count' => $vendorDir . '/symfony/validator/Constraints/Count.php',
'Symfony\\Component\\Validator\\Constraints\\CountValidator' => $vendorDir . '/symfony/validator/Constraints/CountValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Country' => $vendorDir . '/symfony/validator/Constraints/Country.php',
'Symfony\\Component\\Validator\\Constraints\\CountryValidator' => $vendorDir . '/symfony/validator/Constraints/CountryValidator.php',
'Symfony\\Component\\Validator\\Constraints\\CssColor' => $vendorDir . '/symfony/validator/Constraints/CssColor.php',
'Symfony\\Component\\Validator\\Constraints\\CssColorValidator' => $vendorDir . '/symfony/validator/Constraints/CssColorValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Currency' => $vendorDir . '/symfony/validator/Constraints/Currency.php',
'Symfony\\Component\\Validator\\Constraints\\CurrencyValidator' => $vendorDir . '/symfony/validator/Constraints/CurrencyValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Date' => $vendorDir . '/symfony/validator/Constraints/Date.php',
'Symfony\\Component\\Validator\\Constraints\\DateTime' => $vendorDir . '/symfony/validator/Constraints/DateTime.php',
'Symfony\\Component\\Validator\\Constraints\\DateTimeValidator' => $vendorDir . '/symfony/validator/Constraints/DateTimeValidator.php',
'Symfony\\Component\\Validator\\Constraints\\DateValidator' => $vendorDir . '/symfony/validator/Constraints/DateValidator.php',
'Symfony\\Component\\Validator\\Constraints\\DisableAutoMapping' => $vendorDir . '/symfony/validator/Constraints/DisableAutoMapping.php',
'Symfony\\Component\\Validator\\Constraints\\DivisibleBy' => $vendorDir . '/symfony/validator/Constraints/DivisibleBy.php',
'Symfony\\Component\\Validator\\Constraints\\DivisibleByValidator' => $vendorDir . '/symfony/validator/Constraints/DivisibleByValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Email' => $vendorDir . '/symfony/validator/Constraints/Email.php',
'Symfony\\Component\\Validator\\Constraints\\EmailValidator' => $vendorDir . '/symfony/validator/Constraints/EmailValidator.php',
'Symfony\\Component\\Validator\\Constraints\\EnableAutoMapping' => $vendorDir . '/symfony/validator/Constraints/EnableAutoMapping.php',
'Symfony\\Component\\Validator\\Constraints\\EqualTo' => $vendorDir . '/symfony/validator/Constraints/EqualTo.php',
'Symfony\\Component\\Validator\\Constraints\\EqualToValidator' => $vendorDir . '/symfony/validator/Constraints/EqualToValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Existence' => $vendorDir . '/symfony/validator/Constraints/Existence.php',
'Symfony\\Component\\Validator\\Constraints\\Expression' => $vendorDir . '/symfony/validator/Constraints/Expression.php',
'Symfony\\Component\\Validator\\Constraints\\ExpressionLanguageProvider' => $vendorDir . '/symfony/validator/Constraints/ExpressionLanguageProvider.php',
'Symfony\\Component\\Validator\\Constraints\\ExpressionLanguageSyntax' => $vendorDir . '/symfony/validator/Constraints/ExpressionLanguageSyntax.php',
'Symfony\\Component\\Validator\\Constraints\\ExpressionLanguageSyntaxValidator' => $vendorDir . '/symfony/validator/Constraints/ExpressionLanguageSyntaxValidator.php',
'Symfony\\Component\\Validator\\Constraints\\ExpressionSyntax' => $vendorDir . '/symfony/validator/Constraints/ExpressionSyntax.php',
'Symfony\\Component\\Validator\\Constraints\\ExpressionSyntaxValidator' => $vendorDir . '/symfony/validator/Constraints/ExpressionSyntaxValidator.php',
'Symfony\\Component\\Validator\\Constraints\\ExpressionValidator' => $vendorDir . '/symfony/validator/Constraints/ExpressionValidator.php',
'Symfony\\Component\\Validator\\Constraints\\File' => $vendorDir . '/symfony/validator/Constraints/File.php',
'Symfony\\Component\\Validator\\Constraints\\FileValidator' => $vendorDir . '/symfony/validator/Constraints/FileValidator.php',
'Symfony\\Component\\Validator\\Constraints\\GreaterThan' => $vendorDir . '/symfony/validator/Constraints/GreaterThan.php',
'Symfony\\Component\\Validator\\Constraints\\GreaterThanOrEqual' => $vendorDir . '/symfony/validator/Constraints/GreaterThanOrEqual.php',
'Symfony\\Component\\Validator\\Constraints\\GreaterThanOrEqualValidator' => $vendorDir . '/symfony/validator/Constraints/GreaterThanOrEqualValidator.php',
'Symfony\\Component\\Validator\\Constraints\\GreaterThanValidator' => $vendorDir . '/symfony/validator/Constraints/GreaterThanValidator.php',
'Symfony\\Component\\Validator\\Constraints\\GroupSequence' => $vendorDir . '/symfony/validator/Constraints/GroupSequence.php',
'Symfony\\Component\\Validator\\Constraints\\GroupSequenceProvider' => $vendorDir . '/symfony/validator/Constraints/GroupSequenceProvider.php',
'Symfony\\Component\\Validator\\Constraints\\Hostname' => $vendorDir . '/symfony/validator/Constraints/Hostname.php',
'Symfony\\Component\\Validator\\Constraints\\HostnameValidator' => $vendorDir . '/symfony/validator/Constraints/HostnameValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Iban' => $vendorDir . '/symfony/validator/Constraints/Iban.php',
'Symfony\\Component\\Validator\\Constraints\\IbanValidator' => $vendorDir . '/symfony/validator/Constraints/IbanValidator.php',
'Symfony\\Component\\Validator\\Constraints\\IdenticalTo' => $vendorDir . '/symfony/validator/Constraints/IdenticalTo.php',
'Symfony\\Component\\Validator\\Constraints\\IdenticalToValidator' => $vendorDir . '/symfony/validator/Constraints/IdenticalToValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Image' => $vendorDir . '/symfony/validator/Constraints/Image.php',
'Symfony\\Component\\Validator\\Constraints\\ImageValidator' => $vendorDir . '/symfony/validator/Constraints/ImageValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Ip' => $vendorDir . '/symfony/validator/Constraints/Ip.php',
'Symfony\\Component\\Validator\\Constraints\\IpValidator' => $vendorDir . '/symfony/validator/Constraints/IpValidator.php',
'Symfony\\Component\\Validator\\Constraints\\IsFalse' => $vendorDir . '/symfony/validator/Constraints/IsFalse.php',
'Symfony\\Component\\Validator\\Constraints\\IsFalseValidator' => $vendorDir . '/symfony/validator/Constraints/IsFalseValidator.php',
'Symfony\\Component\\Validator\\Constraints\\IsNull' => $vendorDir . '/symfony/validator/Constraints/IsNull.php',
'Symfony\\Component\\Validator\\Constraints\\IsNullValidator' => $vendorDir . '/symfony/validator/Constraints/IsNullValidator.php',
'Symfony\\Component\\Validator\\Constraints\\IsTrue' => $vendorDir . '/symfony/validator/Constraints/IsTrue.php',
'Symfony\\Component\\Validator\\Constraints\\IsTrueValidator' => $vendorDir . '/symfony/validator/Constraints/IsTrueValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Isbn' => $vendorDir . '/symfony/validator/Constraints/Isbn.php',
'Symfony\\Component\\Validator\\Constraints\\IsbnValidator' => $vendorDir . '/symfony/validator/Constraints/IsbnValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Isin' => $vendorDir . '/symfony/validator/Constraints/Isin.php',
'Symfony\\Component\\Validator\\Constraints\\IsinValidator' => $vendorDir . '/symfony/validator/Constraints/IsinValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Issn' => $vendorDir . '/symfony/validator/Constraints/Issn.php',
'Symfony\\Component\\Validator\\Constraints\\IssnValidator' => $vendorDir . '/symfony/validator/Constraints/IssnValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Json' => $vendorDir . '/symfony/validator/Constraints/Json.php',
'Symfony\\Component\\Validator\\Constraints\\JsonValidator' => $vendorDir . '/symfony/validator/Constraints/JsonValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Language' => $vendorDir . '/symfony/validator/Constraints/Language.php',
'Symfony\\Component\\Validator\\Constraints\\LanguageValidator' => $vendorDir . '/symfony/validator/Constraints/LanguageValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Length' => $vendorDir . '/symfony/validator/Constraints/Length.php',
'Symfony\\Component\\Validator\\Constraints\\LengthValidator' => $vendorDir . '/symfony/validator/Constraints/LengthValidator.php',
'Symfony\\Component\\Validator\\Constraints\\LessThan' => $vendorDir . '/symfony/validator/Constraints/LessThan.php',
'Symfony\\Component\\Validator\\Constraints\\LessThanOrEqual' => $vendorDir . '/symfony/validator/Constraints/LessThanOrEqual.php',
'Symfony\\Component\\Validator\\Constraints\\LessThanOrEqualValidator' => $vendorDir . '/symfony/validator/Constraints/LessThanOrEqualValidator.php',
'Symfony\\Component\\Validator\\Constraints\\LessThanValidator' => $vendorDir . '/symfony/validator/Constraints/LessThanValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Locale' => $vendorDir . '/symfony/validator/Constraints/Locale.php',
'Symfony\\Component\\Validator\\Constraints\\LocaleValidator' => $vendorDir . '/symfony/validator/Constraints/LocaleValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Luhn' => $vendorDir . '/symfony/validator/Constraints/Luhn.php',
'Symfony\\Component\\Validator\\Constraints\\LuhnValidator' => $vendorDir . '/symfony/validator/Constraints/LuhnValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Negative' => $vendorDir . '/symfony/validator/Constraints/Negative.php',
'Symfony\\Component\\Validator\\Constraints\\NegativeOrZero' => $vendorDir . '/symfony/validator/Constraints/NegativeOrZero.php',
'Symfony\\Component\\Validator\\Constraints\\NoSuspiciousCharacters' => $vendorDir . '/symfony/validator/Constraints/NoSuspiciousCharacters.php',
'Symfony\\Component\\Validator\\Constraints\\NoSuspiciousCharactersValidator' => $vendorDir . '/symfony/validator/Constraints/NoSuspiciousCharactersValidator.php',
'Symfony\\Component\\Validator\\Constraints\\NotBlank' => $vendorDir . '/symfony/validator/Constraints/NotBlank.php',
'Symfony\\Component\\Validator\\Constraints\\NotBlankValidator' => $vendorDir . '/symfony/validator/Constraints/NotBlankValidator.php',
'Symfony\\Component\\Validator\\Constraints\\NotCompromisedPassword' => $vendorDir . '/symfony/validator/Constraints/NotCompromisedPassword.php',
'Symfony\\Component\\Validator\\Constraints\\NotCompromisedPasswordValidator' => $vendorDir . '/symfony/validator/Constraints/NotCompromisedPasswordValidator.php',
'Symfony\\Component\\Validator\\Constraints\\NotEqualTo' => $vendorDir . '/symfony/validator/Constraints/NotEqualTo.php',
'Symfony\\Component\\Validator\\Constraints\\NotEqualToValidator' => $vendorDir . '/symfony/validator/Constraints/NotEqualToValidator.php',
'Symfony\\Component\\Validator\\Constraints\\NotIdenticalTo' => $vendorDir . '/symfony/validator/Constraints/NotIdenticalTo.php',
'Symfony\\Component\\Validator\\Constraints\\NotIdenticalToValidator' => $vendorDir . '/symfony/validator/Constraints/NotIdenticalToValidator.php',
'Symfony\\Component\\Validator\\Constraints\\NotNull' => $vendorDir . '/symfony/validator/Constraints/NotNull.php',
'Symfony\\Component\\Validator\\Constraints\\NotNullValidator' => $vendorDir . '/symfony/validator/Constraints/NotNullValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Optional' => $vendorDir . '/symfony/validator/Constraints/Optional.php',
'Symfony\\Component\\Validator\\Constraints\\PasswordStrength' => $vendorDir . '/symfony/validator/Constraints/PasswordStrength.php',
'Symfony\\Component\\Validator\\Constraints\\PasswordStrengthValidator' => $vendorDir . '/symfony/validator/Constraints/PasswordStrengthValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Positive' => $vendorDir . '/symfony/validator/Constraints/Positive.php',
'Symfony\\Component\\Validator\\Constraints\\PositiveOrZero' => $vendorDir . '/symfony/validator/Constraints/PositiveOrZero.php',
'Symfony\\Component\\Validator\\Constraints\\Range' => $vendorDir . '/symfony/validator/Constraints/Range.php',
'Symfony\\Component\\Validator\\Constraints\\RangeValidator' => $vendorDir . '/symfony/validator/Constraints/RangeValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Regex' => $vendorDir . '/symfony/validator/Constraints/Regex.php',
'Symfony\\Component\\Validator\\Constraints\\RegexValidator' => $vendorDir . '/symfony/validator/Constraints/RegexValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Required' => $vendorDir . '/symfony/validator/Constraints/Required.php',
'Symfony\\Component\\Validator\\Constraints\\Sequentially' => $vendorDir . '/symfony/validator/Constraints/Sequentially.php',
'Symfony\\Component\\Validator\\Constraints\\SequentiallyValidator' => $vendorDir . '/symfony/validator/Constraints/SequentiallyValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Time' => $vendorDir . '/symfony/validator/Constraints/Time.php',
'Symfony\\Component\\Validator\\Constraints\\TimeValidator' => $vendorDir . '/symfony/validator/Constraints/TimeValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Timezone' => $vendorDir . '/symfony/validator/Constraints/Timezone.php',
'Symfony\\Component\\Validator\\Constraints\\TimezoneValidator' => $vendorDir . '/symfony/validator/Constraints/TimezoneValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Traverse' => $vendorDir . '/symfony/validator/Constraints/Traverse.php',
'Symfony\\Component\\Validator\\Constraints\\Type' => $vendorDir . '/symfony/validator/Constraints/Type.php',
'Symfony\\Component\\Validator\\Constraints\\TypeValidator' => $vendorDir . '/symfony/validator/Constraints/TypeValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Ulid' => $vendorDir . '/symfony/validator/Constraints/Ulid.php',
'Symfony\\Component\\Validator\\Constraints\\UlidValidator' => $vendorDir . '/symfony/validator/Constraints/UlidValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Unique' => $vendorDir . '/symfony/validator/Constraints/Unique.php',
'Symfony\\Component\\Validator\\Constraints\\UniqueValidator' => $vendorDir . '/symfony/validator/Constraints/UniqueValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Url' => $vendorDir . '/symfony/validator/Constraints/Url.php',
'Symfony\\Component\\Validator\\Constraints\\UrlValidator' => $vendorDir . '/symfony/validator/Constraints/UrlValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Uuid' => $vendorDir . '/symfony/validator/Constraints/Uuid.php',
'Symfony\\Component\\Validator\\Constraints\\UuidValidator' => $vendorDir . '/symfony/validator/Constraints/UuidValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Valid' => $vendorDir . '/symfony/validator/Constraints/Valid.php',
'Symfony\\Component\\Validator\\Constraints\\ValidValidator' => $vendorDir . '/symfony/validator/Constraints/ValidValidator.php',
'Symfony\\Component\\Validator\\Constraints\\When' => $vendorDir . '/symfony/validator/Constraints/When.php',
'Symfony\\Component\\Validator\\Constraints\\WhenValidator' => $vendorDir . '/symfony/validator/Constraints/WhenValidator.php',
'Symfony\\Component\\Validator\\Constraints\\ZeroComparisonConstraintTrait' => $vendorDir . '/symfony/validator/Constraints/ZeroComparisonConstraintTrait.php',
'Symfony\\Component\\Validator\\ContainerConstraintValidatorFactory' => $vendorDir . '/symfony/validator/ContainerConstraintValidatorFactory.php',
'Symfony\\Component\\Validator\\Context\\ExecutionContext' => $vendorDir . '/symfony/validator/Context/ExecutionContext.php',
'Symfony\\Component\\Validator\\Context\\ExecutionContextFactory' => $vendorDir . '/symfony/validator/Context/ExecutionContextFactory.php',
'Symfony\\Component\\Validator\\Context\\ExecutionContextFactoryInterface' => $vendorDir . '/symfony/validator/Context/ExecutionContextFactoryInterface.php',
'Symfony\\Component\\Validator\\Context\\ExecutionContextInterface' => $vendorDir . '/symfony/validator/Context/ExecutionContextInterface.php',
'Symfony\\Component\\Validator\\DataCollector\\ValidatorDataCollector' => $vendorDir . '/symfony/validator/DataCollector/ValidatorDataCollector.php',
'Symfony\\Component\\Validator\\DependencyInjection\\AddAutoMappingConfigurationPass' => $vendorDir . '/symfony/validator/DependencyInjection/AddAutoMappingConfigurationPass.php',
'Symfony\\Component\\Validator\\DependencyInjection\\AddConstraintValidatorsPass' => $vendorDir . '/symfony/validator/DependencyInjection/AddConstraintValidatorsPass.php',
'Symfony\\Component\\Validator\\DependencyInjection\\AddValidatorInitializersPass' => $vendorDir . '/symfony/validator/DependencyInjection/AddValidatorInitializersPass.php',
'Symfony\\Component\\Validator\\Exception\\BadMethodCallException' => $vendorDir . '/symfony/validator/Exception/BadMethodCallException.php',
'Symfony\\Component\\Validator\\Exception\\ConstraintDefinitionException' => $vendorDir . '/symfony/validator/Exception/ConstraintDefinitionException.php',
'Symfony\\Component\\Validator\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/validator/Exception/ExceptionInterface.php',
'Symfony\\Component\\Validator\\Exception\\GroupDefinitionException' => $vendorDir . '/symfony/validator/Exception/GroupDefinitionException.php',
'Symfony\\Component\\Validator\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/validator/Exception/InvalidArgumentException.php',
'Symfony\\Component\\Validator\\Exception\\InvalidOptionsException' => $vendorDir . '/symfony/validator/Exception/InvalidOptionsException.php',
'Symfony\\Component\\Validator\\Exception\\LogicException' => $vendorDir . '/symfony/validator/Exception/LogicException.php',
'Symfony\\Component\\Validator\\Exception\\MappingException' => $vendorDir . '/symfony/validator/Exception/MappingException.php',
'Symfony\\Component\\Validator\\Exception\\MissingOptionsException' => $vendorDir . '/symfony/validator/Exception/MissingOptionsException.php',
'Symfony\\Component\\Validator\\Exception\\NoSuchMetadataException' => $vendorDir . '/symfony/validator/Exception/NoSuchMetadataException.php',
'Symfony\\Component\\Validator\\Exception\\OutOfBoundsException' => $vendorDir . '/symfony/validator/Exception/OutOfBoundsException.php',
'Symfony\\Component\\Validator\\Exception\\RuntimeException' => $vendorDir . '/symfony/validator/Exception/RuntimeException.php',
'Symfony\\Component\\Validator\\Exception\\UnexpectedTypeException' => $vendorDir . '/symfony/validator/Exception/UnexpectedTypeException.php',
'Symfony\\Component\\Validator\\Exception\\UnexpectedValueException' => $vendorDir . '/symfony/validator/Exception/UnexpectedValueException.php',
'Symfony\\Component\\Validator\\Exception\\UnsupportedMetadataException' => $vendorDir . '/symfony/validator/Exception/UnsupportedMetadataException.php',
'Symfony\\Component\\Validator\\Exception\\ValidationFailedException' => $vendorDir . '/symfony/validator/Exception/ValidationFailedException.php',
'Symfony\\Component\\Validator\\Exception\\ValidatorException' => $vendorDir . '/symfony/validator/Exception/ValidatorException.php',
'Symfony\\Component\\Validator\\GroupProviderInterface' => $vendorDir . '/symfony/validator/GroupProviderInterface.php',
'Symfony\\Component\\Validator\\GroupSequenceProviderInterface' => $vendorDir . '/symfony/validator/GroupSequenceProviderInterface.php',
'Symfony\\Component\\Validator\\Mapping\\AutoMappingStrategy' => $vendorDir . '/symfony/validator/Mapping/AutoMappingStrategy.php',
'Symfony\\Component\\Validator\\Mapping\\CascadingStrategy' => $vendorDir . '/symfony/validator/Mapping/CascadingStrategy.php',
'Symfony\\Component\\Validator\\Mapping\\ClassMetadata' => $vendorDir . '/symfony/validator/Mapping/ClassMetadata.php',
'Symfony\\Component\\Validator\\Mapping\\ClassMetadataInterface' => $vendorDir . '/symfony/validator/Mapping/ClassMetadataInterface.php',
'Symfony\\Component\\Validator\\Mapping\\Factory\\BlackHoleMetadataFactory' => $vendorDir . '/symfony/validator/Mapping/Factory/BlackHoleMetadataFactory.php',
'Symfony\\Component\\Validator\\Mapping\\Factory\\LazyLoadingMetadataFactory' => $vendorDir . '/symfony/validator/Mapping/Factory/LazyLoadingMetadataFactory.php',
'Symfony\\Component\\Validator\\Mapping\\Factory\\MetadataFactoryInterface' => $vendorDir . '/symfony/validator/Mapping/Factory/MetadataFactoryInterface.php',
'Symfony\\Component\\Validator\\Mapping\\GenericMetadata' => $vendorDir . '/symfony/validator/Mapping/GenericMetadata.php',
'Symfony\\Component\\Validator\\Mapping\\GetterMetadata' => $vendorDir . '/symfony/validator/Mapping/GetterMetadata.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\AbstractLoader' => $vendorDir . '/symfony/validator/Mapping/Loader/AbstractLoader.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\AnnotationLoader' => $vendorDir . '/symfony/validator/Mapping/Loader/AnnotationLoader.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\AttributeLoader' => $vendorDir . '/symfony/validator/Mapping/Loader/AttributeLoader.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\AutoMappingTrait' => $vendorDir . '/symfony/validator/Mapping/Loader/AutoMappingTrait.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\FileLoader' => $vendorDir . '/symfony/validator/Mapping/Loader/FileLoader.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\FilesLoader' => $vendorDir . '/symfony/validator/Mapping/Loader/FilesLoader.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\LoaderChain' => $vendorDir . '/symfony/validator/Mapping/Loader/LoaderChain.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\LoaderInterface' => $vendorDir . '/symfony/validator/Mapping/Loader/LoaderInterface.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\PropertyInfoLoader' => $vendorDir . '/symfony/validator/Mapping/Loader/PropertyInfoLoader.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\StaticMethodLoader' => $vendorDir . '/symfony/validator/Mapping/Loader/StaticMethodLoader.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\XmlFileLoader' => $vendorDir . '/symfony/validator/Mapping/Loader/XmlFileLoader.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\XmlFilesLoader' => $vendorDir . '/symfony/validator/Mapping/Loader/XmlFilesLoader.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\YamlFileLoader' => $vendorDir . '/symfony/validator/Mapping/Loader/YamlFileLoader.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\YamlFilesLoader' => $vendorDir . '/symfony/validator/Mapping/Loader/YamlFilesLoader.php',
'Symfony\\Component\\Validator\\Mapping\\MemberMetadata' => $vendorDir . '/symfony/validator/Mapping/MemberMetadata.php',
'Symfony\\Component\\Validator\\Mapping\\MetadataInterface' => $vendorDir . '/symfony/validator/Mapping/MetadataInterface.php',
'Symfony\\Component\\Validator\\Mapping\\PropertyMetadata' => $vendorDir . '/symfony/validator/Mapping/PropertyMetadata.php',
'Symfony\\Component\\Validator\\Mapping\\PropertyMetadataInterface' => $vendorDir . '/symfony/validator/Mapping/PropertyMetadataInterface.php',
'Symfony\\Component\\Validator\\Mapping\\TraversalStrategy' => $vendorDir . '/symfony/validator/Mapping/TraversalStrategy.php',
'Symfony\\Component\\Validator\\ObjectInitializerInterface' => $vendorDir . '/symfony/validator/ObjectInitializerInterface.php',
'Symfony\\Component\\Validator\\Util\\PropertyPath' => $vendorDir . '/symfony/validator/Util/PropertyPath.php',
'Symfony\\Component\\Validator\\Validation' => $vendorDir . '/symfony/validator/Validation.php',
'Symfony\\Component\\Validator\\ValidatorBuilder' => $vendorDir . '/symfony/validator/ValidatorBuilder.php',
'Symfony\\Component\\Validator\\Validator\\ContextualValidatorInterface' => $vendorDir . '/symfony/validator/Validator/ContextualValidatorInterface.php',
'Symfony\\Component\\Validator\\Validator\\LazyProperty' => $vendorDir . '/symfony/validator/Validator/LazyProperty.php',
'Symfony\\Component\\Validator\\Validator\\RecursiveContextualValidator' => $vendorDir . '/symfony/validator/Validator/RecursiveContextualValidator.php',
'Symfony\\Component\\Validator\\Validator\\RecursiveValidator' => $vendorDir . '/symfony/validator/Validator/RecursiveValidator.php',
'Symfony\\Component\\Validator\\Validator\\TraceableValidator' => $vendorDir . '/symfony/validator/Validator/TraceableValidator.php',
'Symfony\\Component\\Validator\\Validator\\ValidatorInterface' => $vendorDir . '/symfony/validator/Validator/ValidatorInterface.php',
'Symfony\\Component\\Validator\\Violation\\ConstraintViolationBuilder' => $vendorDir . '/symfony/validator/Violation/ConstraintViolationBuilder.php',
'Symfony\\Component\\Validator\\Violation\\ConstraintViolationBuilderInterface' => $vendorDir . '/symfony/validator/Violation/ConstraintViolationBuilderInterface.php',
'Symfony\\Component\\VarDumper\\Caster\\AmqpCaster' => $vendorDir . '/symfony/var-dumper/Caster/AmqpCaster.php',
'Symfony\\Component\\VarDumper\\Caster\\ArgsStub' => $vendorDir . '/symfony/var-dumper/Caster/ArgsStub.php',
'Symfony\\Component\\VarDumper\\Caster\\Caster' => $vendorDir . '/symfony/var-dumper/Caster/Caster.php',

View File

@@ -22,6 +22,7 @@ return array(
'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'),
'Symfony\\Component\\VarExporter\\' => array($vendorDir . '/symfony/var-exporter'),
'Symfony\\Component\\VarDumper\\' => array($vendorDir . '/symfony/var-dumper'),
'Symfony\\Component\\Validator\\' => array($vendorDir . '/symfony/validator'),
'Symfony\\Component\\String\\' => array($vendorDir . '/symfony/string'),
'Symfony\\Component\\Stopwatch\\' => array($vendorDir . '/symfony/stopwatch'),
'Symfony\\Component\\Security\\Csrf\\' => array($vendorDir . '/symfony/security-csrf'),

View File

@@ -49,6 +49,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Symfony\\Component\\Yaml\\' => 23,
'Symfony\\Component\\VarExporter\\' => 30,
'Symfony\\Component\\VarDumper\\' => 28,
'Symfony\\Component\\Validator\\' => 28,
'Symfony\\Component\\String\\' => 25,
'Symfony\\Component\\Stopwatch\\' => 28,
'Symfony\\Component\\Security\\Csrf\\' => 32,
@@ -181,6 +182,10 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
array (
0 => __DIR__ . '/..' . '/symfony/var-dumper',
),
'Symfony\\Component\\Validator\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/validator',
),
'Symfony\\Component\\String\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/string',
@@ -625,6 +630,10 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Toolbar\\ToolbarSpacer\\ToolbarSpacer' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Toolbar/ToolbarSpacer/ToolbarSpacer.php',
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Toolbar\\ToolbarSpacer\\ToolbarSpacerUIBlockFactory' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Toolbar/ToolbarSpacer/ToolbarSpacerUIBlockFactory.php',
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Toolbar\\ToolbarUIBlockFactory' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Toolbar/ToolbarUIBlockFactory.php',
'Combodo\\iTop\\Application\\UI\\Base\\Component\\TurboForm\\TurboForm' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/TurboForm/TurboForm.php',
'Combodo\\iTop\\Application\\UI\\Base\\Component\\TurboForm\\TurboFormUIBlockFactory' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/TurboForm/TurboFormUIBlockFactory.php',
'Combodo\\iTop\\Application\\UI\\Base\\Component\\TurboUpdate\\TurboStream' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/TurboStream/TurboStream.php',
'Combodo\\iTop\\Application\\UI\\Base\\Component\\TurboUpdate\\TurboStreamUIBlockFactory' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/TurboStream/TurboStreamUIBlockFactory.php',
'Combodo\\iTop\\Application\\UI\\Base\\Layout\\ActivityPanel\\ActivityEntry\\ActivityEntry' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Layout/ActivityPanel/ActivityEntry/ActivityEntry.php',
'Combodo\\iTop\\Application\\UI\\Base\\Layout\\ActivityPanel\\ActivityEntry\\ActivityEntryFactory' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Layout/ActivityPanel/ActivityEntry/ActivityEntryFactory.php',
'Combodo\\iTop\\Application\\UI\\Base\\Layout\\ActivityPanel\\ActivityEntry\\CMDBChangeOp\\CMDBChangeOpAttachmentAddedFactory' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Layout/ActivityPanel/ActivityEntry/CMDBChangeOp/CMDBChangeOpAttachmentAddedFactory.php',
@@ -722,6 +731,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Combodo\\iTop\\Controller\\AjaxRenderController' => __DIR__ . '/../..' . '/sources/Controller/AjaxRenderController.php',
'Combodo\\iTop\\Controller\\Base\\Layout\\ActivityPanelController' => __DIR__ . '/../..' . '/sources/Controller/Base/Layout/ActivityPanelController.php',
'Combodo\\iTop\\Controller\\Base\\Layout\\ObjectController' => __DIR__ . '/../..' . '/sources/Controller/Base/Layout/ObjectController.php',
'Combodo\\iTop\\Controller\\Base\\Layout\\OqlController' => __DIR__ . '/../..' . '/sources/Controller/Base/Layout/OqlController.php',
'Combodo\\iTop\\Controller\\Links\\LinkSetController' => __DIR__ . '/../..' . '/sources/Controller/Links/LinkSetController.php',
'Combodo\\iTop\\Controller\\Newsroom\\iTopNewsroomController' => __DIR__ . '/../..' . '/sources/Controller/Newsroom/iTopNewsroomController.php',
'Combodo\\iTop\\Controller\\Notifications\\ActionController' => __DIR__ . '/../..' . '/sources/Controller/Notifications/ActionController.php',
@@ -854,8 +864,103 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Combodo\\iTop\\Form\\Validator\\MultipleChoicesValidator' => __DIR__ . '/../..' . '/sources/Form/Validator/MultipleChoicesValidator.php',
'Combodo\\iTop\\Form\\Validator\\NotEmptyExtKeyValidator' => __DIR__ . '/../..' . '/sources/Form/Validator/NotEmptyExtKeyValidator.php',
'Combodo\\iTop\\Form\\Validator\\SelectObjectValidator' => __DIR__ . '/../..' . '/sources/Form/Validator/SelectObjectValidator.php',
'Combodo\\iTop\\Forms\\Block\\AbstractFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/AbstractFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\AbstractTypeFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/AbstractTypeFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\CheckboxFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Base/CheckboxFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\ChoiceFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Base/ChoiceFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\ChoiceFromInputsBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Base/ChoiceFromInputsBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\CollectionBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Base/CollectionBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\DateFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Base/DateFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\DateTimeFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Base/DateTimeFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\FormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Base/FormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\HiddenFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Base/HiddenFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\IntegerFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Base/IntegerFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\NumberFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Base/NumberFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\TextAreaFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Base/TextAreaFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\TextFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Base/TextFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\DataModel\\AttributeChoiceFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/DataModel/AttributeChoiceFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\DataModel\\AttributeTypeChoiceFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/DataModel/AttributeTypeChoiceFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\DataModel\\AttributeValueChoiceFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/DataModel/AttributeValueChoiceFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\DataModel\\Dashlet\\AggregateFunctionFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/DataModel/Dashlet/AggregateFunctionFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\DataModel\\Dashlet\\ClassAttributeGroupByFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/DataModel/Dashlet/ClassAttributeGroupByFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\DataModel\\LabelFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/DataModel/LabelFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\DataModel\\OqlFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/DataModel/OqlFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Expression\\AbstractExpressionFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Expression/AbstractExpressionFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Expression\\BooleanExpressionFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Expression/BooleanExpressionFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Expression\\NumberExpressionFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Expression/NumberExpressionFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Expression\\StringExpressionFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Expression/StringExpressionFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\FormBlockException' => __DIR__ . '/../..' . '/sources/Forms/Block/FormBlockException.php',
'Combodo\\iTop\\Forms\\Block\\FormBlockHelper' => __DIR__ . '/../..' . '/sources/Forms/Block/FormBlockHelper.php',
'Combodo\\iTop\\Forms\\Block\\FormBlockService' => __DIR__ . '/../..' . '/sources/Forms/Block/FormBlockService.php',
'Combodo\\iTop\\Forms\\Block\\IFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/IFormBlock.php',
'Combodo\\iTop\\Forms\\Compiler\\FormsCompiler' => __DIR__ . '/../..' . '/sources/Forms/Compiler/FormsCompiler.php',
'Combodo\\iTop\\Forms\\Compiler\\FormsCompilerException' => __DIR__ . '/../..' . '/sources/Forms/Compiler/FormsCompilerException.php',
'Combodo\\iTop\\Forms\\Controller\\FormsController' => __DIR__ . '/../..' . '/sources/Forms/Controller/FormsController.php',
'Combodo\\iTop\\Forms\\FormBuilder\\DependencyHandler' => __DIR__ . '/../..' . '/sources/Forms/FormBuilder/DependencyHandler.php',
'Combodo\\iTop\\Forms\\FormBuilder\\DependencyMap' => __DIR__ . '/../..' . '/sources/Forms/FormBuilder/DependencyMap.php',
'Combodo\\iTop\\Forms\\FormBuilder\\FormBuilder' => __DIR__ . '/../..' . '/sources/Forms/FormBuilder/FormBuilder.php',
'Combodo\\iTop\\Forms\\FormBuilder\\FormBuilderException' => __DIR__ . '/../..' . '/sources/Forms/FormBuilder/FormBuilderException.php',
'Combodo\\iTop\\Forms\\FormBuilder\\FormHelper' => __DIR__ . '/../..' . '/sources/Forms/FormBuilder/FormHelper.php',
'Combodo\\iTop\\Forms\\FormBuilder\\FormTypeExtension' => __DIR__ . '/../..' . '/sources/Forms/FormBuilder/FormTypeExtension.php',
'Combodo\\iTop\\Forms\\FormBuilder\\ResolvedFormType' => __DIR__ . '/../..' . '/sources/Forms/FormBuilder/ResolvedFormType.php',
'Combodo\\iTop\\Forms\\FormBuilder\\ResolvedFormTypeFactory' => __DIR__ . '/../..' . '/sources/Forms/FormBuilder/ResolvedFormTypeFactory.php',
'Combodo\\iTop\\Forms\\FormType\\Base\\ChoiceFormType' => __DIR__ . '/../..' . '/sources/Forms/FormType/Base/ChoiceFormType.php',
'Combodo\\iTop\\Forms\\FormType\\Base\\CollectionFormType' => __DIR__ . '/../..' . '/sources/Forms/FormType/Base/CollectionFormType.php',
'Combodo\\iTop\\Forms\\FormType\\Base\\FormType' => __DIR__ . '/../..' . '/sources/Forms/FormType/Base/FormType.php',
'Combodo\\iTop\\Forms\\FormType\\DataModel\\OqlFormType' => __DIR__ . '/../..' . '/sources/Forms/FormType/DataModel/OqlFormType.php',
'Combodo\\iTop\\Forms\\FormType\\FormTypeHelper' => __DIR__ . '/../..' . '/sources/Forms/FormType/FormTypeHelper.php',
'Combodo\\iTop\\Forms\\Forms' => __DIR__ . '/../..' . '/sources/Forms/Forms.php',
'Combodo\\iTop\\Forms\\FormsException' => __DIR__ . '/../..' . '/sources/Forms/FormsException.php',
'Combodo\\iTop\\Forms\\IO\\AbstractFormIO' => __DIR__ . '/../..' . '/sources/Forms/IO/AbstractFormIO.php',
'Combodo\\iTop\\Forms\\IO\\Converter\\AbstractConverter' => __DIR__ . '/../..' . '/sources/Forms/IO/Converter/AbstractConverter.php',
'Combodo\\iTop\\Forms\\IO\\Converter\\ChoiceValueToLabelConverter' => __DIR__ . '/../..' . '/sources/Forms/IO/Converter/ChoiceValueToLabelConverter.php',
'Combodo\\iTop\\Forms\\IO\\Converter\\CollectionToCountConverter' => __DIR__ . '/../..' . '/sources/Forms/IO/Converter/CollectionToCountConverter.php',
'Combodo\\iTop\\Forms\\IO\\Converter\\OqlToClassConverter' => __DIR__ . '/../..' . '/sources/Forms/IO/Converter/OqlToClassConverter.php',
'Combodo\\iTop\\Forms\\IO\\FormBinding' => __DIR__ . '/../..' . '/sources/Forms/IO/FormBinding.php',
'Combodo\\iTop\\Forms\\IO\\FormBlockIOException' => __DIR__ . '/../..' . '/sources/Forms/IO/FormBlockIOException.php',
'Combodo\\iTop\\Forms\\IO\\FormInput' => __DIR__ . '/../..' . '/sources/Forms/IO/FormInput.php',
'Combodo\\iTop\\Forms\\IO\\FormOutput' => __DIR__ . '/../..' . '/sources/Forms/IO/FormOutput.php',
'Combodo\\iTop\\Forms\\IO\\Format\\AbstractIOFormat' => __DIR__ . '/../..' . '/sources/Forms/IO/Format/AbstractIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\AttributeIOFormat' => __DIR__ . '/../..' . '/sources/Forms/IO/Format/AttributeIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\AttributeTypeArrayIOFormat' => __DIR__ . '/../..' . '/sources/Forms/IO/Format/AttributeTypeArrayIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\AttributeTypeIOFormat' => __DIR__ . '/../..' . '/sources/Forms/IO/Format/AttributeTypeIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\BooleanIOFormat' => __DIR__ . '/../..' . '/sources/Forms/IO/Format/BooleanIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\ClassIOFormat' => __DIR__ . '/../..' . '/sources/Forms/IO/Format/ClassIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\IntegerIOFormat' => __DIR__ . '/../..' . '/sources/Forms/IO/Format/IntegerIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\NumberIOFormat' => __DIR__ . '/../..' . '/sources/Forms/IO/Format/NumberIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\StringIOFormat' => __DIR__ . '/../..' . '/sources/Forms/IO/Format/StringIOFormat.php',
'Combodo\\iTop\\Forms\\Register\\IORegister' => __DIR__ . '/../..' . '/sources/Forms/Register/IORegister.php',
'Combodo\\iTop\\Forms\\Register\\Option' => __DIR__ . '/../..' . '/sources/Forms/Register/Option.php',
'Combodo\\iTop\\Forms\\Register\\OptionsRegister' => __DIR__ . '/../..' . '/sources/Forms/Register/OptionsRegister.php',
'Combodo\\iTop\\Forms\\Register\\RegisterException' => __DIR__ . '/../..' . '/sources/Forms/Register/RegisterException.php',
'Combodo\\iTop\\Forms\\Twig\\Extension\\FormCompatibilityExtension' => __DIR__ . '/../..' . '/sources/Forms/Twig/Extension/FormCompatibilityExtension.php',
'Combodo\\iTop\\Forms\\Validator\\AttributeExist' => __DIR__ . '/../..' . '/sources/Forms/Validator/AttributeExist.php',
'Combodo\\iTop\\Forms\\Validator\\AttributeExistValidator' => __DIR__ . '/../..' . '/sources/Forms/Validator/AttributeExistValidator.php',
'Combodo\\iTop\\PhpParser\\Evaluation\\PhpExpressionEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/PhpExpressionEvaluator.php',
'Combodo\\iTop\\PropertyTree\\AbstractProperty' => __DIR__ . '/../..' . '/sources/PropertyTree/AbstractProperty.php',
'Combodo\\iTop\\PropertyTree\\Property' => __DIR__ . '/../..' . '/sources/PropertyTree/Property.php',
'Combodo\\iTop\\PropertyTree\\PropertyTree' => __DIR__ . '/../..' . '/sources/PropertyTree/PropertyTree.php',
'Combodo\\iTop\\PropertyTree\\PropertyTreeDesign' => __DIR__ . '/../..' . '/sources/PropertyTree/PropertyTreeDesign.php',
'Combodo\\iTop\\PropertyTree\\PropertyTreeException' => __DIR__ . '/../..' . '/sources/PropertyTree/PropertyTreeException.php',
'Combodo\\iTop\\PropertyTree\\PropertyTreeFactory' => __DIR__ . '/../..' . '/sources/PropertyTree/PropertyTreeFactory.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\AbstractValueType' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/AbstractValueType.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeAggregateFunction' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeAggregateFunction.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeBoolean' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeBoolean.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeChoice' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeChoice.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeChoiceFromInput' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeChoiceFromInput.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClass' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeClass.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClassAttribute' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeClassAttribute.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClassAttributeGroupBy' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeClassAttributeGroupBy.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClassAttributeValue' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeClassAttributeValue.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeCollection' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeCollection.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeFactory' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeFactory.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeIcon' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeIcon.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeInteger' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeInteger.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeLabel' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeLabel.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeOQL' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeOQL.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeProfileName' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeProfileName.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeString' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeString.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeText' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeText.php',
'Combodo\\iTop\\Renderer\\BlockRenderer' => __DIR__ . '/../..' . '/sources/Renderer/BlockRenderer.php',
'Combodo\\iTop\\Renderer\\Bootstrap\\BsFieldRendererMappings' => __DIR__ . '/../..' . '/sources/Renderer/Bootstrap/BsFieldRendererMappings.php',
'Combodo\\iTop\\Renderer\\Bootstrap\\BsFormRenderer' => __DIR__ . '/../..' . '/sources/Renderer/Bootstrap/BsFormRenderer.php',
@@ -878,6 +983,8 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Combodo\\iTop\\Service\\Base\\ObjectRepository' => __DIR__ . '/../..' . '/sources/Service/Base/ObjectRepository.php',
'Combodo\\iTop\\Service\\Base\\iDataPostProcessor' => __DIR__ . '/../..' . '/sources/Service/Base/iDataPostProcessor.php',
'Combodo\\iTop\\Service\\Cache\\DataModelDependantCache' => __DIR__ . '/../..' . '/sources/Service/Cache/DataModelDependantCache.php',
'Combodo\\iTop\\Service\\DependencyInjection\\DIException' => __DIR__ . '/../..' . '/sources/Service/DependencyInjection/DIException.php',
'Combodo\\iTop\\Service\\DependencyInjection\\DIService' => __DIR__ . '/../..' . '/sources/Service/DependencyInjection/DIService.php',
'Combodo\\iTop\\Service\\Events\\Description\\EventDataDescription' => __DIR__ . '/../..' . '/sources/Service/Events/Description/EventDataDescription.php',
'Combodo\\iTop\\Service\\Events\\Description\\EventDescription' => __DIR__ . '/../..' . '/sources/Service/Events/Description/EventDescription.php',
'Combodo\\iTop\\Service\\Events\\EventData' => __DIR__ . '/../..' . '/sources/Service/Events/EventData.php',
@@ -3452,6 +3559,227 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Symfony\\Component\\String\\Slugger\\AsciiSlugger' => __DIR__ . '/..' . '/symfony/string/Slugger/AsciiSlugger.php',
'Symfony\\Component\\String\\Slugger\\SluggerInterface' => __DIR__ . '/..' . '/symfony/string/Slugger/SluggerInterface.php',
'Symfony\\Component\\String\\UnicodeString' => __DIR__ . '/..' . '/symfony/string/UnicodeString.php',
'Symfony\\Component\\Validator\\Attribute\\HasNamedArguments' => __DIR__ . '/..' . '/symfony/validator/Attribute/HasNamedArguments.php',
'Symfony\\Component\\Validator\\Command\\DebugCommand' => __DIR__ . '/..' . '/symfony/validator/Command/DebugCommand.php',
'Symfony\\Component\\Validator\\Constraint' => __DIR__ . '/..' . '/symfony/validator/Constraint.php',
'Symfony\\Component\\Validator\\ConstraintValidator' => __DIR__ . '/..' . '/symfony/validator/ConstraintValidator.php',
'Symfony\\Component\\Validator\\ConstraintValidatorFactory' => __DIR__ . '/..' . '/symfony/validator/ConstraintValidatorFactory.php',
'Symfony\\Component\\Validator\\ConstraintValidatorFactoryInterface' => __DIR__ . '/..' . '/symfony/validator/ConstraintValidatorFactoryInterface.php',
'Symfony\\Component\\Validator\\ConstraintValidatorInterface' => __DIR__ . '/..' . '/symfony/validator/ConstraintValidatorInterface.php',
'Symfony\\Component\\Validator\\ConstraintViolation' => __DIR__ . '/..' . '/symfony/validator/ConstraintViolation.php',
'Symfony\\Component\\Validator\\ConstraintViolationInterface' => __DIR__ . '/..' . '/symfony/validator/ConstraintViolationInterface.php',
'Symfony\\Component\\Validator\\ConstraintViolationList' => __DIR__ . '/..' . '/symfony/validator/ConstraintViolationList.php',
'Symfony\\Component\\Validator\\ConstraintViolationListInterface' => __DIR__ . '/..' . '/symfony/validator/ConstraintViolationListInterface.php',
'Symfony\\Component\\Validator\\Constraints\\AbstractComparison' => __DIR__ . '/..' . '/symfony/validator/Constraints/AbstractComparison.php',
'Symfony\\Component\\Validator\\Constraints\\AbstractComparisonValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/AbstractComparisonValidator.php',
'Symfony\\Component\\Validator\\Constraints\\All' => __DIR__ . '/..' . '/symfony/validator/Constraints/All.php',
'Symfony\\Component\\Validator\\Constraints\\AllValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/AllValidator.php',
'Symfony\\Component\\Validator\\Constraints\\AtLeastOneOf' => __DIR__ . '/..' . '/symfony/validator/Constraints/AtLeastOneOf.php',
'Symfony\\Component\\Validator\\Constraints\\AtLeastOneOfValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/AtLeastOneOfValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Bic' => __DIR__ . '/..' . '/symfony/validator/Constraints/Bic.php',
'Symfony\\Component\\Validator\\Constraints\\BicValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/BicValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Blank' => __DIR__ . '/..' . '/symfony/validator/Constraints/Blank.php',
'Symfony\\Component\\Validator\\Constraints\\BlankValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/BlankValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Callback' => __DIR__ . '/..' . '/symfony/validator/Constraints/Callback.php',
'Symfony\\Component\\Validator\\Constraints\\CallbackValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/CallbackValidator.php',
'Symfony\\Component\\Validator\\Constraints\\CardScheme' => __DIR__ . '/..' . '/symfony/validator/Constraints/CardScheme.php',
'Symfony\\Component\\Validator\\Constraints\\CardSchemeValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/CardSchemeValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Cascade' => __DIR__ . '/..' . '/symfony/validator/Constraints/Cascade.php',
'Symfony\\Component\\Validator\\Constraints\\Choice' => __DIR__ . '/..' . '/symfony/validator/Constraints/Choice.php',
'Symfony\\Component\\Validator\\Constraints\\ChoiceValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/ChoiceValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Cidr' => __DIR__ . '/..' . '/symfony/validator/Constraints/Cidr.php',
'Symfony\\Component\\Validator\\Constraints\\CidrValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/CidrValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Collection' => __DIR__ . '/..' . '/symfony/validator/Constraints/Collection.php',
'Symfony\\Component\\Validator\\Constraints\\CollectionValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/CollectionValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Composite' => __DIR__ . '/..' . '/symfony/validator/Constraints/Composite.php',
'Symfony\\Component\\Validator\\Constraints\\Compound' => __DIR__ . '/..' . '/symfony/validator/Constraints/Compound.php',
'Symfony\\Component\\Validator\\Constraints\\CompoundValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/CompoundValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Count' => __DIR__ . '/..' . '/symfony/validator/Constraints/Count.php',
'Symfony\\Component\\Validator\\Constraints\\CountValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/CountValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Country' => __DIR__ . '/..' . '/symfony/validator/Constraints/Country.php',
'Symfony\\Component\\Validator\\Constraints\\CountryValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/CountryValidator.php',
'Symfony\\Component\\Validator\\Constraints\\CssColor' => __DIR__ . '/..' . '/symfony/validator/Constraints/CssColor.php',
'Symfony\\Component\\Validator\\Constraints\\CssColorValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/CssColorValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Currency' => __DIR__ . '/..' . '/symfony/validator/Constraints/Currency.php',
'Symfony\\Component\\Validator\\Constraints\\CurrencyValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/CurrencyValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Date' => __DIR__ . '/..' . '/symfony/validator/Constraints/Date.php',
'Symfony\\Component\\Validator\\Constraints\\DateTime' => __DIR__ . '/..' . '/symfony/validator/Constraints/DateTime.php',
'Symfony\\Component\\Validator\\Constraints\\DateTimeValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/DateTimeValidator.php',
'Symfony\\Component\\Validator\\Constraints\\DateValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/DateValidator.php',
'Symfony\\Component\\Validator\\Constraints\\DisableAutoMapping' => __DIR__ . '/..' . '/symfony/validator/Constraints/DisableAutoMapping.php',
'Symfony\\Component\\Validator\\Constraints\\DivisibleBy' => __DIR__ . '/..' . '/symfony/validator/Constraints/DivisibleBy.php',
'Symfony\\Component\\Validator\\Constraints\\DivisibleByValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/DivisibleByValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Email' => __DIR__ . '/..' . '/symfony/validator/Constraints/Email.php',
'Symfony\\Component\\Validator\\Constraints\\EmailValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/EmailValidator.php',
'Symfony\\Component\\Validator\\Constraints\\EnableAutoMapping' => __DIR__ . '/..' . '/symfony/validator/Constraints/EnableAutoMapping.php',
'Symfony\\Component\\Validator\\Constraints\\EqualTo' => __DIR__ . '/..' . '/symfony/validator/Constraints/EqualTo.php',
'Symfony\\Component\\Validator\\Constraints\\EqualToValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/EqualToValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Existence' => __DIR__ . '/..' . '/symfony/validator/Constraints/Existence.php',
'Symfony\\Component\\Validator\\Constraints\\Expression' => __DIR__ . '/..' . '/symfony/validator/Constraints/Expression.php',
'Symfony\\Component\\Validator\\Constraints\\ExpressionLanguageProvider' => __DIR__ . '/..' . '/symfony/validator/Constraints/ExpressionLanguageProvider.php',
'Symfony\\Component\\Validator\\Constraints\\ExpressionLanguageSyntax' => __DIR__ . '/..' . '/symfony/validator/Constraints/ExpressionLanguageSyntax.php',
'Symfony\\Component\\Validator\\Constraints\\ExpressionLanguageSyntaxValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/ExpressionLanguageSyntaxValidator.php',
'Symfony\\Component\\Validator\\Constraints\\ExpressionSyntax' => __DIR__ . '/..' . '/symfony/validator/Constraints/ExpressionSyntax.php',
'Symfony\\Component\\Validator\\Constraints\\ExpressionSyntaxValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/ExpressionSyntaxValidator.php',
'Symfony\\Component\\Validator\\Constraints\\ExpressionValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/ExpressionValidator.php',
'Symfony\\Component\\Validator\\Constraints\\File' => __DIR__ . '/..' . '/symfony/validator/Constraints/File.php',
'Symfony\\Component\\Validator\\Constraints\\FileValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/FileValidator.php',
'Symfony\\Component\\Validator\\Constraints\\GreaterThan' => __DIR__ . '/..' . '/symfony/validator/Constraints/GreaterThan.php',
'Symfony\\Component\\Validator\\Constraints\\GreaterThanOrEqual' => __DIR__ . '/..' . '/symfony/validator/Constraints/GreaterThanOrEqual.php',
'Symfony\\Component\\Validator\\Constraints\\GreaterThanOrEqualValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/GreaterThanOrEqualValidator.php',
'Symfony\\Component\\Validator\\Constraints\\GreaterThanValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/GreaterThanValidator.php',
'Symfony\\Component\\Validator\\Constraints\\GroupSequence' => __DIR__ . '/..' . '/symfony/validator/Constraints/GroupSequence.php',
'Symfony\\Component\\Validator\\Constraints\\GroupSequenceProvider' => __DIR__ . '/..' . '/symfony/validator/Constraints/GroupSequenceProvider.php',
'Symfony\\Component\\Validator\\Constraints\\Hostname' => __DIR__ . '/..' . '/symfony/validator/Constraints/Hostname.php',
'Symfony\\Component\\Validator\\Constraints\\HostnameValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/HostnameValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Iban' => __DIR__ . '/..' . '/symfony/validator/Constraints/Iban.php',
'Symfony\\Component\\Validator\\Constraints\\IbanValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/IbanValidator.php',
'Symfony\\Component\\Validator\\Constraints\\IdenticalTo' => __DIR__ . '/..' . '/symfony/validator/Constraints/IdenticalTo.php',
'Symfony\\Component\\Validator\\Constraints\\IdenticalToValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/IdenticalToValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Image' => __DIR__ . '/..' . '/symfony/validator/Constraints/Image.php',
'Symfony\\Component\\Validator\\Constraints\\ImageValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/ImageValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Ip' => __DIR__ . '/..' . '/symfony/validator/Constraints/Ip.php',
'Symfony\\Component\\Validator\\Constraints\\IpValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/IpValidator.php',
'Symfony\\Component\\Validator\\Constraints\\IsFalse' => __DIR__ . '/..' . '/symfony/validator/Constraints/IsFalse.php',
'Symfony\\Component\\Validator\\Constraints\\IsFalseValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/IsFalseValidator.php',
'Symfony\\Component\\Validator\\Constraints\\IsNull' => __DIR__ . '/..' . '/symfony/validator/Constraints/IsNull.php',
'Symfony\\Component\\Validator\\Constraints\\IsNullValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/IsNullValidator.php',
'Symfony\\Component\\Validator\\Constraints\\IsTrue' => __DIR__ . '/..' . '/symfony/validator/Constraints/IsTrue.php',
'Symfony\\Component\\Validator\\Constraints\\IsTrueValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/IsTrueValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Isbn' => __DIR__ . '/..' . '/symfony/validator/Constraints/Isbn.php',
'Symfony\\Component\\Validator\\Constraints\\IsbnValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/IsbnValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Isin' => __DIR__ . '/..' . '/symfony/validator/Constraints/Isin.php',
'Symfony\\Component\\Validator\\Constraints\\IsinValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/IsinValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Issn' => __DIR__ . '/..' . '/symfony/validator/Constraints/Issn.php',
'Symfony\\Component\\Validator\\Constraints\\IssnValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/IssnValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Json' => __DIR__ . '/..' . '/symfony/validator/Constraints/Json.php',
'Symfony\\Component\\Validator\\Constraints\\JsonValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/JsonValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Language' => __DIR__ . '/..' . '/symfony/validator/Constraints/Language.php',
'Symfony\\Component\\Validator\\Constraints\\LanguageValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/LanguageValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Length' => __DIR__ . '/..' . '/symfony/validator/Constraints/Length.php',
'Symfony\\Component\\Validator\\Constraints\\LengthValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/LengthValidator.php',
'Symfony\\Component\\Validator\\Constraints\\LessThan' => __DIR__ . '/..' . '/symfony/validator/Constraints/LessThan.php',
'Symfony\\Component\\Validator\\Constraints\\LessThanOrEqual' => __DIR__ . '/..' . '/symfony/validator/Constraints/LessThanOrEqual.php',
'Symfony\\Component\\Validator\\Constraints\\LessThanOrEqualValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/LessThanOrEqualValidator.php',
'Symfony\\Component\\Validator\\Constraints\\LessThanValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/LessThanValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Locale' => __DIR__ . '/..' . '/symfony/validator/Constraints/Locale.php',
'Symfony\\Component\\Validator\\Constraints\\LocaleValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/LocaleValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Luhn' => __DIR__ . '/..' . '/symfony/validator/Constraints/Luhn.php',
'Symfony\\Component\\Validator\\Constraints\\LuhnValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/LuhnValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Negative' => __DIR__ . '/..' . '/symfony/validator/Constraints/Negative.php',
'Symfony\\Component\\Validator\\Constraints\\NegativeOrZero' => __DIR__ . '/..' . '/symfony/validator/Constraints/NegativeOrZero.php',
'Symfony\\Component\\Validator\\Constraints\\NoSuspiciousCharacters' => __DIR__ . '/..' . '/symfony/validator/Constraints/NoSuspiciousCharacters.php',
'Symfony\\Component\\Validator\\Constraints\\NoSuspiciousCharactersValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/NoSuspiciousCharactersValidator.php',
'Symfony\\Component\\Validator\\Constraints\\NotBlank' => __DIR__ . '/..' . '/symfony/validator/Constraints/NotBlank.php',
'Symfony\\Component\\Validator\\Constraints\\NotBlankValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/NotBlankValidator.php',
'Symfony\\Component\\Validator\\Constraints\\NotCompromisedPassword' => __DIR__ . '/..' . '/symfony/validator/Constraints/NotCompromisedPassword.php',
'Symfony\\Component\\Validator\\Constraints\\NotCompromisedPasswordValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/NotCompromisedPasswordValidator.php',
'Symfony\\Component\\Validator\\Constraints\\NotEqualTo' => __DIR__ . '/..' . '/symfony/validator/Constraints/NotEqualTo.php',
'Symfony\\Component\\Validator\\Constraints\\NotEqualToValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/NotEqualToValidator.php',
'Symfony\\Component\\Validator\\Constraints\\NotIdenticalTo' => __DIR__ . '/..' . '/symfony/validator/Constraints/NotIdenticalTo.php',
'Symfony\\Component\\Validator\\Constraints\\NotIdenticalToValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/NotIdenticalToValidator.php',
'Symfony\\Component\\Validator\\Constraints\\NotNull' => __DIR__ . '/..' . '/symfony/validator/Constraints/NotNull.php',
'Symfony\\Component\\Validator\\Constraints\\NotNullValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/NotNullValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Optional' => __DIR__ . '/..' . '/symfony/validator/Constraints/Optional.php',
'Symfony\\Component\\Validator\\Constraints\\PasswordStrength' => __DIR__ . '/..' . '/symfony/validator/Constraints/PasswordStrength.php',
'Symfony\\Component\\Validator\\Constraints\\PasswordStrengthValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/PasswordStrengthValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Positive' => __DIR__ . '/..' . '/symfony/validator/Constraints/Positive.php',
'Symfony\\Component\\Validator\\Constraints\\PositiveOrZero' => __DIR__ . '/..' . '/symfony/validator/Constraints/PositiveOrZero.php',
'Symfony\\Component\\Validator\\Constraints\\Range' => __DIR__ . '/..' . '/symfony/validator/Constraints/Range.php',
'Symfony\\Component\\Validator\\Constraints\\RangeValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/RangeValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Regex' => __DIR__ . '/..' . '/symfony/validator/Constraints/Regex.php',
'Symfony\\Component\\Validator\\Constraints\\RegexValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/RegexValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Required' => __DIR__ . '/..' . '/symfony/validator/Constraints/Required.php',
'Symfony\\Component\\Validator\\Constraints\\Sequentially' => __DIR__ . '/..' . '/symfony/validator/Constraints/Sequentially.php',
'Symfony\\Component\\Validator\\Constraints\\SequentiallyValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/SequentiallyValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Time' => __DIR__ . '/..' . '/symfony/validator/Constraints/Time.php',
'Symfony\\Component\\Validator\\Constraints\\TimeValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/TimeValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Timezone' => __DIR__ . '/..' . '/symfony/validator/Constraints/Timezone.php',
'Symfony\\Component\\Validator\\Constraints\\TimezoneValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/TimezoneValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Traverse' => __DIR__ . '/..' . '/symfony/validator/Constraints/Traverse.php',
'Symfony\\Component\\Validator\\Constraints\\Type' => __DIR__ . '/..' . '/symfony/validator/Constraints/Type.php',
'Symfony\\Component\\Validator\\Constraints\\TypeValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/TypeValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Ulid' => __DIR__ . '/..' . '/symfony/validator/Constraints/Ulid.php',
'Symfony\\Component\\Validator\\Constraints\\UlidValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/UlidValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Unique' => __DIR__ . '/..' . '/symfony/validator/Constraints/Unique.php',
'Symfony\\Component\\Validator\\Constraints\\UniqueValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/UniqueValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Url' => __DIR__ . '/..' . '/symfony/validator/Constraints/Url.php',
'Symfony\\Component\\Validator\\Constraints\\UrlValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/UrlValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Uuid' => __DIR__ . '/..' . '/symfony/validator/Constraints/Uuid.php',
'Symfony\\Component\\Validator\\Constraints\\UuidValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/UuidValidator.php',
'Symfony\\Component\\Validator\\Constraints\\Valid' => __DIR__ . '/..' . '/symfony/validator/Constraints/Valid.php',
'Symfony\\Component\\Validator\\Constraints\\ValidValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/ValidValidator.php',
'Symfony\\Component\\Validator\\Constraints\\When' => __DIR__ . '/..' . '/symfony/validator/Constraints/When.php',
'Symfony\\Component\\Validator\\Constraints\\WhenValidator' => __DIR__ . '/..' . '/symfony/validator/Constraints/WhenValidator.php',
'Symfony\\Component\\Validator\\Constraints\\ZeroComparisonConstraintTrait' => __DIR__ . '/..' . '/symfony/validator/Constraints/ZeroComparisonConstraintTrait.php',
'Symfony\\Component\\Validator\\ContainerConstraintValidatorFactory' => __DIR__ . '/..' . '/symfony/validator/ContainerConstraintValidatorFactory.php',
'Symfony\\Component\\Validator\\Context\\ExecutionContext' => __DIR__ . '/..' . '/symfony/validator/Context/ExecutionContext.php',
'Symfony\\Component\\Validator\\Context\\ExecutionContextFactory' => __DIR__ . '/..' . '/symfony/validator/Context/ExecutionContextFactory.php',
'Symfony\\Component\\Validator\\Context\\ExecutionContextFactoryInterface' => __DIR__ . '/..' . '/symfony/validator/Context/ExecutionContextFactoryInterface.php',
'Symfony\\Component\\Validator\\Context\\ExecutionContextInterface' => __DIR__ . '/..' . '/symfony/validator/Context/ExecutionContextInterface.php',
'Symfony\\Component\\Validator\\DataCollector\\ValidatorDataCollector' => __DIR__ . '/..' . '/symfony/validator/DataCollector/ValidatorDataCollector.php',
'Symfony\\Component\\Validator\\DependencyInjection\\AddAutoMappingConfigurationPass' => __DIR__ . '/..' . '/symfony/validator/DependencyInjection/AddAutoMappingConfigurationPass.php',
'Symfony\\Component\\Validator\\DependencyInjection\\AddConstraintValidatorsPass' => __DIR__ . '/..' . '/symfony/validator/DependencyInjection/AddConstraintValidatorsPass.php',
'Symfony\\Component\\Validator\\DependencyInjection\\AddValidatorInitializersPass' => __DIR__ . '/..' . '/symfony/validator/DependencyInjection/AddValidatorInitializersPass.php',
'Symfony\\Component\\Validator\\Exception\\BadMethodCallException' => __DIR__ . '/..' . '/symfony/validator/Exception/BadMethodCallException.php',
'Symfony\\Component\\Validator\\Exception\\ConstraintDefinitionException' => __DIR__ . '/..' . '/symfony/validator/Exception/ConstraintDefinitionException.php',
'Symfony\\Component\\Validator\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/validator/Exception/ExceptionInterface.php',
'Symfony\\Component\\Validator\\Exception\\GroupDefinitionException' => __DIR__ . '/..' . '/symfony/validator/Exception/GroupDefinitionException.php',
'Symfony\\Component\\Validator\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/symfony/validator/Exception/InvalidArgumentException.php',
'Symfony\\Component\\Validator\\Exception\\InvalidOptionsException' => __DIR__ . '/..' . '/symfony/validator/Exception/InvalidOptionsException.php',
'Symfony\\Component\\Validator\\Exception\\LogicException' => __DIR__ . '/..' . '/symfony/validator/Exception/LogicException.php',
'Symfony\\Component\\Validator\\Exception\\MappingException' => __DIR__ . '/..' . '/symfony/validator/Exception/MappingException.php',
'Symfony\\Component\\Validator\\Exception\\MissingOptionsException' => __DIR__ . '/..' . '/symfony/validator/Exception/MissingOptionsException.php',
'Symfony\\Component\\Validator\\Exception\\NoSuchMetadataException' => __DIR__ . '/..' . '/symfony/validator/Exception/NoSuchMetadataException.php',
'Symfony\\Component\\Validator\\Exception\\OutOfBoundsException' => __DIR__ . '/..' . '/symfony/validator/Exception/OutOfBoundsException.php',
'Symfony\\Component\\Validator\\Exception\\RuntimeException' => __DIR__ . '/..' . '/symfony/validator/Exception/RuntimeException.php',
'Symfony\\Component\\Validator\\Exception\\UnexpectedTypeException' => __DIR__ . '/..' . '/symfony/validator/Exception/UnexpectedTypeException.php',
'Symfony\\Component\\Validator\\Exception\\UnexpectedValueException' => __DIR__ . '/..' . '/symfony/validator/Exception/UnexpectedValueException.php',
'Symfony\\Component\\Validator\\Exception\\UnsupportedMetadataException' => __DIR__ . '/..' . '/symfony/validator/Exception/UnsupportedMetadataException.php',
'Symfony\\Component\\Validator\\Exception\\ValidationFailedException' => __DIR__ . '/..' . '/symfony/validator/Exception/ValidationFailedException.php',
'Symfony\\Component\\Validator\\Exception\\ValidatorException' => __DIR__ . '/..' . '/symfony/validator/Exception/ValidatorException.php',
'Symfony\\Component\\Validator\\GroupProviderInterface' => __DIR__ . '/..' . '/symfony/validator/GroupProviderInterface.php',
'Symfony\\Component\\Validator\\GroupSequenceProviderInterface' => __DIR__ . '/..' . '/symfony/validator/GroupSequenceProviderInterface.php',
'Symfony\\Component\\Validator\\Mapping\\AutoMappingStrategy' => __DIR__ . '/..' . '/symfony/validator/Mapping/AutoMappingStrategy.php',
'Symfony\\Component\\Validator\\Mapping\\CascadingStrategy' => __DIR__ . '/..' . '/symfony/validator/Mapping/CascadingStrategy.php',
'Symfony\\Component\\Validator\\Mapping\\ClassMetadata' => __DIR__ . '/..' . '/symfony/validator/Mapping/ClassMetadata.php',
'Symfony\\Component\\Validator\\Mapping\\ClassMetadataInterface' => __DIR__ . '/..' . '/symfony/validator/Mapping/ClassMetadataInterface.php',
'Symfony\\Component\\Validator\\Mapping\\Factory\\BlackHoleMetadataFactory' => __DIR__ . '/..' . '/symfony/validator/Mapping/Factory/BlackHoleMetadataFactory.php',
'Symfony\\Component\\Validator\\Mapping\\Factory\\LazyLoadingMetadataFactory' => __DIR__ . '/..' . '/symfony/validator/Mapping/Factory/LazyLoadingMetadataFactory.php',
'Symfony\\Component\\Validator\\Mapping\\Factory\\MetadataFactoryInterface' => __DIR__ . '/..' . '/symfony/validator/Mapping/Factory/MetadataFactoryInterface.php',
'Symfony\\Component\\Validator\\Mapping\\GenericMetadata' => __DIR__ . '/..' . '/symfony/validator/Mapping/GenericMetadata.php',
'Symfony\\Component\\Validator\\Mapping\\GetterMetadata' => __DIR__ . '/..' . '/symfony/validator/Mapping/GetterMetadata.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\AbstractLoader' => __DIR__ . '/..' . '/symfony/validator/Mapping/Loader/AbstractLoader.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\AnnotationLoader' => __DIR__ . '/..' . '/symfony/validator/Mapping/Loader/AnnotationLoader.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\AttributeLoader' => __DIR__ . '/..' . '/symfony/validator/Mapping/Loader/AttributeLoader.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\AutoMappingTrait' => __DIR__ . '/..' . '/symfony/validator/Mapping/Loader/AutoMappingTrait.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\FileLoader' => __DIR__ . '/..' . '/symfony/validator/Mapping/Loader/FileLoader.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\FilesLoader' => __DIR__ . '/..' . '/symfony/validator/Mapping/Loader/FilesLoader.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\LoaderChain' => __DIR__ . '/..' . '/symfony/validator/Mapping/Loader/LoaderChain.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\LoaderInterface' => __DIR__ . '/..' . '/symfony/validator/Mapping/Loader/LoaderInterface.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\PropertyInfoLoader' => __DIR__ . '/..' . '/symfony/validator/Mapping/Loader/PropertyInfoLoader.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\StaticMethodLoader' => __DIR__ . '/..' . '/symfony/validator/Mapping/Loader/StaticMethodLoader.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\XmlFileLoader' => __DIR__ . '/..' . '/symfony/validator/Mapping/Loader/XmlFileLoader.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\XmlFilesLoader' => __DIR__ . '/..' . '/symfony/validator/Mapping/Loader/XmlFilesLoader.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\YamlFileLoader' => __DIR__ . '/..' . '/symfony/validator/Mapping/Loader/YamlFileLoader.php',
'Symfony\\Component\\Validator\\Mapping\\Loader\\YamlFilesLoader' => __DIR__ . '/..' . '/symfony/validator/Mapping/Loader/YamlFilesLoader.php',
'Symfony\\Component\\Validator\\Mapping\\MemberMetadata' => __DIR__ . '/..' . '/symfony/validator/Mapping/MemberMetadata.php',
'Symfony\\Component\\Validator\\Mapping\\MetadataInterface' => __DIR__ . '/..' . '/symfony/validator/Mapping/MetadataInterface.php',
'Symfony\\Component\\Validator\\Mapping\\PropertyMetadata' => __DIR__ . '/..' . '/symfony/validator/Mapping/PropertyMetadata.php',
'Symfony\\Component\\Validator\\Mapping\\PropertyMetadataInterface' => __DIR__ . '/..' . '/symfony/validator/Mapping/PropertyMetadataInterface.php',
'Symfony\\Component\\Validator\\Mapping\\TraversalStrategy' => __DIR__ . '/..' . '/symfony/validator/Mapping/TraversalStrategy.php',
'Symfony\\Component\\Validator\\ObjectInitializerInterface' => __DIR__ . '/..' . '/symfony/validator/ObjectInitializerInterface.php',
'Symfony\\Component\\Validator\\Util\\PropertyPath' => __DIR__ . '/..' . '/symfony/validator/Util/PropertyPath.php',
'Symfony\\Component\\Validator\\Validation' => __DIR__ . '/..' . '/symfony/validator/Validation.php',
'Symfony\\Component\\Validator\\ValidatorBuilder' => __DIR__ . '/..' . '/symfony/validator/ValidatorBuilder.php',
'Symfony\\Component\\Validator\\Validator\\ContextualValidatorInterface' => __DIR__ . '/..' . '/symfony/validator/Validator/ContextualValidatorInterface.php',
'Symfony\\Component\\Validator\\Validator\\LazyProperty' => __DIR__ . '/..' . '/symfony/validator/Validator/LazyProperty.php',
'Symfony\\Component\\Validator\\Validator\\RecursiveContextualValidator' => __DIR__ . '/..' . '/symfony/validator/Validator/RecursiveContextualValidator.php',
'Symfony\\Component\\Validator\\Validator\\RecursiveValidator' => __DIR__ . '/..' . '/symfony/validator/Validator/RecursiveValidator.php',
'Symfony\\Component\\Validator\\Validator\\TraceableValidator' => __DIR__ . '/..' . '/symfony/validator/Validator/TraceableValidator.php',
'Symfony\\Component\\Validator\\Validator\\ValidatorInterface' => __DIR__ . '/..' . '/symfony/validator/Validator/ValidatorInterface.php',
'Symfony\\Component\\Validator\\Violation\\ConstraintViolationBuilder' => __DIR__ . '/..' . '/symfony/validator/Violation/ConstraintViolationBuilder.php',
'Symfony\\Component\\Validator\\Violation\\ConstraintViolationBuilderInterface' => __DIR__ . '/..' . '/symfony/validator/Violation/ConstraintViolationBuilderInterface.php',
'Symfony\\Component\\VarDumper\\Caster\\AmqpCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/AmqpCaster.php',
'Symfony\\Component\\VarDumper\\Caster\\ArgsStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/ArgsStub.php',
'Symfony\\Component\\VarDumper\\Caster\\Caster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/Caster.php',

View File

@@ -5238,6 +5238,110 @@
],
"install-path": "../symfony/twig-bundle"
},
{
"name": "symfony/validator",
"version": "v6.4.29",
"version_normalized": "6.4.29.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/validator.git",
"reference": "99df8a769e64e399f510166141ea74f450e8dd1d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/validator/zipball/99df8a769e64e399f510166141ea74f450e8dd1d",
"reference": "99df8a769e64e399f510166141ea74f450e8dd1d",
"shasum": ""
},
"require": {
"php": ">=8.1",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-mbstring": "~1.0",
"symfony/polyfill-php83": "^1.27",
"symfony/translation-contracts": "^2.5|^3"
},
"conflict": {
"doctrine/annotations": "<1.13",
"doctrine/lexer": "<1.1",
"symfony/dependency-injection": "<5.4",
"symfony/expression-language": "<5.4",
"symfony/http-kernel": "<5.4",
"symfony/intl": "<5.4",
"symfony/property-info": "<5.4",
"symfony/translation": "<5.4.35|>=6.0,<6.3.12|>=6.4,<6.4.3|>=7.0,<7.0.3",
"symfony/yaml": "<5.4"
},
"require-dev": {
"doctrine/annotations": "^1.13|^2",
"egulias/email-validator": "^2.1.10|^3|^4",
"symfony/cache": "^5.4|^6.0|^7.0",
"symfony/config": "^5.4|^6.0|^7.0",
"symfony/console": "^5.4|^6.0|^7.0",
"symfony/dependency-injection": "^5.4|^6.0|^7.0",
"symfony/expression-language": "^5.4|^6.0|^7.0",
"symfony/finder": "^5.4|^6.0|^7.0",
"symfony/http-client": "^5.4|^6.0|^7.0",
"symfony/http-foundation": "^5.4|^6.0|^7.0",
"symfony/http-kernel": "^5.4|^6.0|^7.0",
"symfony/intl": "^5.4|^6.0|^7.0",
"symfony/mime": "^5.4|^6.0|^7.0",
"symfony/property-access": "^5.4|^6.0|^7.0",
"symfony/property-info": "^5.4|^6.0|^7.0",
"symfony/translation": "^5.4.35|~6.3.12|^6.4.3|^7.0.3",
"symfony/yaml": "^5.4|^6.0|^7.0"
},
"time": "2025-11-06T20:26:06+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Symfony\\Component\\Validator\\": ""
},
"exclude-from-classmap": [
"/Tests/",
"/Resources/bin/"
]
},
"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 tools to validate values",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/validator/tree/v6.4.29"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"install-path": "../symfony/validator"
},
{
"name": "symfony/var-dumper",
"version": "v6.4.26",

View File

@@ -3,7 +3,7 @@
'name' => 'combodo/itop',
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'reference' => '19d062aa830b6d6c7d17ac4046fc9ee2c5e3fab1',
'reference' => '06e5c8078690380fb8690371a69eb3c68469c1ed',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -22,7 +22,7 @@
'combodo/itop' => array(
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'reference' => '19d062aa830b6d6c7d17ac4046fc9ee2c5e3fab1',
'reference' => '06e5c8078690380fb8690371a69eb3c68469c1ed',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -678,6 +678,15 @@
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/validator' => array(
'pretty_version' => 'v6.4.29',
'version' => '6.4.29.0',
'reference' => '99df8a769e64e399f510166141ea74f450e8dd1d',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/validator',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/var-dumper' => array(
'pretty_version' => 'v6.4.26',
'version' => '6.4.26.0',

View File

@@ -0,0 +1,7 @@
parameters:
level: 6
errorFormat: raw
editorUrl: '%%file%% %%line%% %%column%%: %%error%%'
paths:
- src
- tests

View File

@@ -26,192 +26,192 @@ use Symfony\Component\Form\Extension\Core\Type\TextType;
*/
class FormBuilder extends FormConfigBuilder implements \IteratorAggregate, FormBuilderInterface
{
/**
* The children of the form builder.
*
* @var FormBuilderInterface[]
*/
private array $children = [];
/**
* The children of the form builder.
*
* @var FormBuilderInterface[]
*/
private array $children = [];
/**
* The data of children who haven't been converted to form builders yet.
*/
private array $unresolvedChildren = [];
/**
* The data of children who haven't been converted to form builders yet.
*/
private array $unresolvedChildren = [];
public function __construct(?string $name, ?string $dataClass, EventDispatcherInterface $dispatcher, FormFactoryInterface $factory, array $options = [])
{
parent::__construct($name, $dataClass, $dispatcher, $options);
public function __construct(?string $name, ?string $dataClass, EventDispatcherInterface $dispatcher, FormFactoryInterface $factory, array $options = [])
{
parent::__construct($name, $dataClass, $dispatcher, $options);
$this->setFormFactory($factory);
}
$this->setFormFactory($factory);
}
public function add(FormBuilderInterface|string $child, ?string $type = null, array $options = []): static
{
if ($this->locked) {
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
public function add(FormBuilderInterface|string $child, ?string $type = null, array $options = []): static
{
if ($this->locked) {
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
if ($child instanceof FormBuilderInterface) {
$this->children[$child->getName()] = $child;
if ($child instanceof FormBuilderInterface) {
$this->children[$child->getName()] = $child;
// In case an unresolved child with the same name exists
unset($this->unresolvedChildren[$child->getName()]);
// In case an unresolved child with the same name exists
unset($this->unresolvedChildren[$child->getName()]);
return $this;
}
return $this;
}
if (!\is_string($child) && !\is_int($child)) {
throw new UnexpectedTypeException($child, 'string or Symfony\Component\Form\FormBuilderInterface');
}
if (!\is_string($child) && !\is_int($child)) {
throw new UnexpectedTypeException($child, 'string or Symfony\Component\Form\FormBuilderInterface');
}
// Add to "children" to maintain order
$this->children[$child] = null;
$this->unresolvedChildren[$child] = [$type, $options];
// Add to "children" to maintain order
$this->children[$child] = null;
$this->unresolvedChildren[$child] = [$type, $options];
return $this;
}
return $this;
}
public function create(string $name, ?string $type = null, array $options = []): FormBuilderInterface
{
if ($this->locked) {
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
public function create(string $name, ?string $type = null, array $options = []): FormBuilderInterface
{
if ($this->locked) {
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
if (null === $type && null === $this->getDataClass()) {
$type = TextType::class;
}
if (null === $type && null === $this->getDataClass()) {
$type = TextType::class;
}
if (null !== $type) {
return $this->getFormFactory()->createNamedBuilder($name, $type, null, $options);
}
if (null !== $type) {
return $this->getFormFactory()->createNamedBuilder($name, $type, null, $options);
}
return $this->getFormFactory()->createBuilderForProperty($this->getDataClass(), $name, null, $options);
}
return $this->getFormFactory()->createBuilderForProperty($this->getDataClass(), $name, null, $options);
}
public function get(string $name): FormBuilderInterface
{
if ($this->locked) {
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
public function get(string $name): FormBuilderInterface
{
if ($this->locked) {
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
if (isset($this->unresolvedChildren[$name])) {
return $this->resolveChild($name);
}
if (isset($this->unresolvedChildren[$name])) {
return $this->resolveChild($name);
}
if (isset($this->children[$name])) {
return $this->children[$name];
}
if (isset($this->children[$name])) {
return $this->children[$name];
}
throw new InvalidArgumentException(\sprintf('The child with the name "%s" does not exist.', $name));
}
throw new InvalidArgumentException(\sprintf('The child with the name "%s" does not exist.', $name));
}
public function remove(string $name): static
{
if ($this->locked) {
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
public function remove(string $name): static
{
if ($this->locked) {
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
unset($this->unresolvedChildren[$name], $this->children[$name]);
unset($this->unresolvedChildren[$name], $this->children[$name]);
return $this;
}
return $this;
}
public function has(string $name): bool
{
if ($this->locked) {
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
public function has(string $name): bool
{
if ($this->locked) {
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
return isset($this->unresolvedChildren[$name]) || isset($this->children[$name]);
}
return isset($this->unresolvedChildren[$name]) || isset($this->children[$name]);
}
public function all(): array
{
if ($this->locked) {
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
public function all(): array
{
if ($this->locked) {
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
$this->resolveChildren();
$this->resolveChildren();
return $this->children;
}
return $this->children;
}
public function count(): int
{
if ($this->locked) {
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
public function count(): int
{
if ($this->locked) {
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
return \count($this->children);
}
return \count($this->children);
}
public function getFormConfig(): FormConfigInterface
{
/** @var self $config */
$config = parent::getFormConfig();
public function getFormConfig(): FormConfigInterface
{
/** @var self $config */
$config = parent::getFormConfig();
$config->children = [];
$config->unresolvedChildren = [];
$config->children = [];
$config->unresolvedChildren = [];
return $config;
}
return $config;
}
public function getForm(): FormInterface
{
if ($this->locked) {
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
public function getForm(): FormInterface
{
if ($this->locked) {
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
$this->resolveChildren();
$this->resolveChildren();
$form = new Form($this->getFormConfig());
$form = new Form($this->getFormConfig());
foreach ($this->children as $child) {
// Automatic initialization is only supported on root forms
$form->add($child->setAutoInitialize(false)->getForm());
}
foreach ($this->children as $child) {
// Automatic initialization is only supported on root forms
$form->add($child->setAutoInitialize(false)->getForm());
}
if ($this->getAutoInitialize()) {
// Automatically initialize the form if it is configured so
$form->initialize();
}
if ($this->getAutoInitialize()) {
// Automatically initialize the form if it is configured so
$form->initialize();
}
return $form;
}
return $form;
}
/**
* @return \Traversable<string, FormBuilderInterface>
*/
public function getIterator(): \Traversable
{
if ($this->locked) {
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
/**
* @return \Traversable<string, FormBuilderInterface>
*/
public function getIterator(): \Traversable
{
if ($this->locked) {
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
return new \ArrayIterator($this->all());
}
return new \ArrayIterator($this->all());
}
/**
* Converts an unresolved child into a {@link FormBuilderInterface} instance.
*/
private function resolveChild(string $name): FormBuilderInterface
{
[$type, $options] = $this->unresolvedChildren[$name];
/**
* Converts an unresolved child into a {@link FormBuilderInterface} instance.
*/
private function resolveChild(string $name): FormBuilderInterface
{
[$type, $options] = $this->unresolvedChildren[$name];
unset($this->unresolvedChildren[$name]);
unset($this->unresolvedChildren[$name]);
return $this->children[$name] = $this->create($name, $type, $options);
}
return $this->children[$name] = $this->create($name, $type, $options);
}
/**
* Converts all unresolved children into {@link FormBuilder} instances.
*/
private function resolveChildren(): void
{
foreach ($this->unresolvedChildren as $name => $info) {
$this->children[$name] = $this->create($name, $info[0], $info[1]);
}
/**
* Converts all unresolved children into {@link FormBuilder} instances.
*/
private function resolveChildren(): void
{
foreach ($this->unresolvedChildren as $name => $info) {
$this->children[$name] = $this->create($name, $info[0], $info[1]);
}
$this->unresolvedChildren = [];
}
$this->unresolvedChildren = [];
}
}

19
lib/symfony/mime/LICENSE Normal file
View File

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

View File

@@ -0,0 +1,17 @@
<?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\Component\Validator\Attribute;
#[\Attribute(\Attribute::TARGET_METHOD)]
final class HasNamedArguments
{
}

View File

@@ -0,0 +1,415 @@
CHANGELOG
=========
6.4
---
* Add `is_valid` function to the `Expression` constraint, its behavior is the same as `ValidatorInterface::validate`
* Allow single integer for the `versions` option of the `Uuid` constraint
* Allow single constraint to be passed to the `constraints` option of the `When` constraint
* Deprecate Doctrine annotations support in favor of native attributes
* Deprecate `ValidatorBuilder::setDoctrineAnnotationReader()`
* Deprecate `ValidatorBuilder::addDefaultDoctrineAnnotationReader()`
* Add `number`, `finite-number` and `finite-float` types to `Type` constraint
* Add the `withSeconds` option to the `Time` constraint that allows to pass time without seconds
* Deprecate `ValidatorBuilder::enableAnnotationMapping()`, use `ValidatorBuilder::enableAttributeMapping()` instead
* Deprecate `ValidatorBuilder::disableAnnotationMapping()`, use `ValidatorBuilder::disableAttributeMapping()` instead
* Deprecate `AnnotationLoader`, use `AttributeLoader` instead
* Add `GroupProviderInterface` to implement validation group providers outside the underlying class
6.3
---
* Add method `getConstraint()` to `ConstraintViolationInterface`
* Add `Uuid::TIME_BASED_VERSIONS` to match that a UUID being validated embeds a timestamp
* Add the `pattern` parameter in violations of the `Regex` constraint
* Add a `NoSuspiciousCharacters` constraint to validate a string is not a spoofing attempt
* Add a `PasswordStrength` constraint to check the strength of a password
* Add the `countUnit` option to the `Length` constraint to allow counting the string length either by code points (like before, now the default setting `Length::COUNT_CODEPOINTS`), bytes (`Length::COUNT_BYTES`) or graphemes (`Length::COUNT_GRAPHEMES`)
* Add the `filenameMaxLength` option to the `File` constraint
* Add the `exclude` option to the `Cascade` constraint
* Add the `value_length` parameter to `Length` constraint
* Allow to disable the translation domain for `ConstraintViolationInterface` messages
6.2
---
* Add option `Email::VALIDATION_MODE_HTML5_ALLOW_NO_TLD` with "html5-allow-no-tld" e-mail validation mode, to match with the W3C official specification
* Add method `getCause()` to `ConstraintViolationInterface`
* Add the `When` constraint and validator
* Deprecate the "loose" e-mail validation mode, use "html5" instead
* Add the `negate` option to the `Expression` constraint, to inverse the logic of the violation's creation
* Add the `extensions` option to the `File` constraint as an alternative to `mimeTypes` which checks the mime type of the file, its extension, and the consistency between them
* Add padding for enhanced privacy to the `NotCompromisedPasswordValidator`
6.1
---
* Add the `fields` option to the `Unique` constraint, to define which collection keys should be checked for uniqueness
* Deprecate `Constraint::$errorNames`, use `Constraint::ERROR_NAMES` instead
* Deprecate constraint `ExpressionLanguageSyntax`, use `ExpressionSyntax` instead
* Add method `__toString()` to `ConstraintViolationInterface` & `ConstraintViolationListInterface`
* Allow creating constraints with required arguments
* Add the `match` option to the `Choice` constraint
6.0
---
* Remove the `allowEmptyString` option from the `Length` constraint
* Remove the `NumberConstraintTrait` trait
* `ValidatorBuilder::enableAnnotationMapping()` does not accept a Doctrine annotation reader anymore
* `ValidatorBuilder::enableAnnotationMapping()` won't automatically setup a Doctrine annotation reader anymore
5.4
---
* Add a `Cidr` constraint to validate CIDR notations
* Add a `CssColor` constraint to validate CSS colors
* Add support for `ConstraintViolationList::createFromMessage()`
* Add error's uid to `Count` and `Length` constraints with "exactly" option enabled
5.3
---
* Add the `normalizer` option to the `Unique` constraint
* Add `Validation::createIsValidCallable()` that returns true/false instead of throwing exceptions
5.2.0
-----
* added a `Cascade` constraint to ease validating nested typed object properties
* deprecated the `allowEmptyString` option of the `Length` constraint
Before:
```php
use Symfony\Component\Validator\Constraints as Assert;
/**
* @Assert\Length(min=5, allowEmptyString=true)
*/
```
After:
```php
use Symfony\Component\Validator\Constraints as Assert;
/**
* @Assert\AtLeastOneOf({
* @Assert\Blank(),
* @Assert\Length(min=5)
* })
*/
```
* added the `Isin` constraint and validator
* added the `ULID` constraint and validator
* added support for UUIDv6 in `Uuid` constraint
* enabled the validator to load constraints from PHP attributes
* deprecated the `NumberConstraintTrait` trait
* deprecated setting or creating a Doctrine annotation reader via `ValidatorBuilder::enableAnnotationMapping()`, pass `true` as first parameter and additionally call `setDoctrineAnnotationReader()` or `addDefaultDoctrineAnnotationReader()` to set up the annotation reader
5.1.0
-----
* Add `AtLeastOneOf` constraint that is considered to be valid if at least one of the nested constraints is valid
* added the `Hostname` constraint and validator
* added the `alpha3` option to the `Country` and `Language` constraints
* allow to define a reusable set of constraints by extending the `Compound` constraint
* added `Sequentially` constraint, to sequentially validate a set of constraints (any violation raised will prevent further validation of the nested constraints)
* added the `divisibleBy` option to the `Count` constraint
* added the `ExpressionLanguageSyntax` constraint
5.0.0
-----
* an `ExpressionLanguage` instance or null must be passed as the first argument of `ExpressionValidator::__construct()`
* removed the `checkDNS` and `dnsMessage` options of the `Url` constraint
* removed the `checkMX`, `checkHost` and `strict` options of the `Email` constraint
* removed support for validating instances of `\DateTimeInterface` in `DateTimeValidator`, `DateValidator` and `TimeValidator`
* removed support for using the `Bic`, `Country`, `Currency`, `Language` and `Locale` constraints without `symfony/intl`
* removed support for using the `Email` constraint without `egulias/email-validator`
* removed support for using the `Expression` constraint without `symfony/expression-language`
* changed default value of `canonicalize` option of `Locale` constraint to `true`
* removed `ValidatorBuilderInterface`
* passing a null message when instantiating a `ConstraintViolation` is not allowed
* changed the default value of `Length::$allowEmptyString` to `false` and made it optional
* removed `Symfony\Component\Validator\Mapping\Cache\CacheInterface` in favor of PSR-6.
* removed `ValidatorBuilder::setMetadataCache`, use `ValidatorBuilder::setMappingCache` instead.
4.4.0
-----
* [BC BREAK] using null as `$classValidatorRegexp` value in `PropertyInfoLoader::__construct` will not enable auto-mapping for all classes anymore, use `'{.*}'` instead.
* added `EnableAutoMapping` and `DisableAutoMapping` constraints to enable or disable auto mapping for class or a property
* using anything else than a `string` as the code of a `ConstraintViolation` is deprecated, a `string` type-hint will
be added to the constructor of the `ConstraintViolation` class and to the `ConstraintViolationBuilder::setCode()`
method in 5.0
* deprecated passing an `ExpressionLanguage` instance as the second argument of `ExpressionValidator::__construct()`. Pass it as the first argument instead.
* added the `compared_value_path` parameter in violations when using any
comparison constraint with the `propertyPath` option.
* added support for checking an array of types in `TypeValidator`
* added a new `allowEmptyString` option to the `Length` constraint to allow rejecting empty strings when `min` is set, by setting it to `false`.
* Added new `minPropertyPath` and `maxPropertyPath` options
to `Range` constraint in order to get the value to compare
from an array or object
* added the `min_limit_path` and `max_limit_path` parameters in violations when using
`Range` constraint with respectively the `minPropertyPath` and
`maxPropertyPath` options
* added a new `notInRangeMessage` option to the `Range` constraint that will
be used in the violation builder when both `min` and `max` are not null
* added ability to use stringable objects as violation messages
* Overriding the methods `ConstraintValidatorTestCase::setUp()` and `ConstraintValidatorTestCase::tearDown()` without the `void` return-type is deprecated.
* deprecated `Symfony\Component\Validator\Mapping\Cache\CacheInterface` in favor of PSR-6.
* deprecated `ValidatorBuilder::setMetadataCache`, use `ValidatorBuilder::setMappingCache` instead.
* Marked the `ValidatorDataCollector` class as `@final`.
4.3.0
-----
* added `Timezone` constraint
* added `NotCompromisedPassword` constraint
* added options `iban` and `ibanPropertyPath` to Bic constraint
* added UATP cards support to `CardSchemeValidator`
* added option `allowNull` to NotBlank constraint
* added `Json` constraint
* added `Unique` constraint
* added a new `normalizer` option to the string constraints and to the `NotBlank` constraint
* added `Positive` constraint
* added `PositiveOrZero` constraint
* added `Negative` constraint
* added `NegativeOrZero` constraint
4.2.0
-----
* added a new `UnexpectedValueException` that can be thrown by constraint validators, these exceptions are caught by
the validator and are converted into constraint violations
* added `DivisibleBy` constraint
* decoupled from `symfony/translation` by using `Symfony\Contracts\Translation\TranslatorInterface`
* deprecated `ValidatorBuilderInterface`
* made `ValidatorBuilder::setTranslator()` final
* marked `format` the default option in `DateTime` constraint
* deprecated validating instances of `\DateTimeInterface` in `DateTimeValidator`, `DateValidator` and `TimeValidator`.
* deprecated using the `Bic`, `Country`, `Currency`, `Language` and `Locale` constraints without `symfony/intl`
* deprecated using the `Email` constraint without `egulias/email-validator`
* deprecated using the `Expression` constraint without `symfony/expression-language`
4.1.0
-----
* Deprecated the `checkDNS` and `dnsMessage` options of the `Url` constraint.
* added a `values` option to the `Expression` constraint
* Deprecated use of `Locale` constraint without setting `true` at "canonicalize" option, which will be the default value in 5.0
4.0.0
-----
* Setting the `strict` option of the `Choice` constraint to anything but `true`
is not supported anymore.
* removed the `DateTimeValidator::PATTERN` constant
* removed the `AbstractConstraintValidatorTest` class
* removed support for setting the `checkDNS` option of the `Url` constraint to `true`
3.4.0
-----
* added support for validation groups to the `Valid` constraint
* not setting the `strict` option of the `Choice` constraint to `true` is
deprecated and will throw an exception in Symfony 4.0
* setting the `checkDNS` option of the `Url` constraint to `true` is deprecated in favor of
the `Url::CHECK_DNS_TYPE_*` constants values and will throw an exception in Symfony 4.0
* added min/max amount of pixels check to `Image` constraint via `minPixels` and `maxPixels`
* added a new "propertyPath" option to comparison constraints in order to get the value to compare from an array or object
3.3.0
-----
* added `AddValidatorInitializersPass`
* added `AddConstraintValidatorsPass`
* added `ContainerConstraintValidatorFactory`
3.2.0
-----
* deprecated `Tests\Constraints\AbstractConstraintValidatorTest` in favor of `Test\ConstraintValidatorTestCase`
* added support for PHP constants in YAML configuration files
3.1.0
-----
* deprecated `DateTimeValidator::PATTERN` constant
* added a `format` option to the `DateTime` constraint
2.8.0
-----
* added the BIC (SWIFT-Code) validator
2.7.0
-----
* deprecated `DefaultTranslator` in favor of `Symfony\Component\Translation\IdentityTranslator`
* deprecated PHP7-incompatible constraints (Null, True, False) and related validators (NullValidator, TrueValidator, FalseValidator) in favor of their `Is`-prefixed equivalent
2.6.0
-----
* [BC BREAK] `FileValidator` disallow empty files
* [BC BREAK] `UserPasswordValidator` source message change
* [BC BREAK] added internal `ExecutionContextInterface::setConstraint()`
* added `ConstraintViolation::getConstraint()`
* [BC BREAK] The `ExpressionValidator` will now evaluate the Expression even when the property value is null or an empty string
* deprecated `ClassMetadata::hasMemberMetadatas()`
* deprecated `ClassMetadata::getMemberMetadatas()`
* deprecated `ClassMetadata::addMemberMetadata()`
* [BC BREAK] added `Mapping\MetadataInterface::getConstraints()`
* added generic "payload" option to all constraints for attaching domain-specific data
* [BC BREAK] added `ConstraintViolationBuilderInterface::setCause()`
2.5.0
-----
* deprecated `ApcCache` in favor of `DoctrineCache`
* added `DoctrineCache` to adapt any Doctrine cache
* `GroupSequence` now implements `ArrayAccess`, `Countable` and `Traversable`
* [BC BREAK] changed `ClassMetadata::getGroupSequence()` to return a `GroupSequence` instance instead of an array
* `Callback` can now be put onto properties (useful when you pass a closure to the constraint)
* deprecated `ClassBasedInterface`
* deprecated `MetadataInterface`
* deprecated `PropertyMetadataInterface`
* deprecated `PropertyMetadataContainerInterface`
* deprecated `Mapping\ElementMetadata`
* added `Mapping\MetadataInterface`
* added `Mapping\ClassMetadataInterface`
* added `Mapping\PropertyMetadataInterface`
* added `Mapping\GenericMetadata`
* added `Mapping\CascadingStrategy`
* added `Mapping\TraversalStrategy`
* deprecated `Mapping\ClassMetadata::accept()`
* deprecated `Mapping\MemberMetadata::accept()`
* removed array type hint of `Mapping\ClassMetadata::setGroupSequence()`
* deprecated `MetadataFactoryInterface`
* deprecated `Mapping\BlackholeMetadataFactory`
* deprecated `Mapping\ClassMetadataFactory`
* added `Mapping\Factory\MetadataFactoryInterface`
* added `Mapping\Factory\BlackHoleMetadataFactory`
* added `Mapping\Factory\LazyLoadingMetadataFactory`
* deprecated `ExecutionContextInterface`
* deprecated `ExecutionContext`
* deprecated `GlobalExecutionContextInterface`
* added `Context\ExecutionContextInterface`
* added `Context\ExecutionContext`
* added `Context\ExecutionContextFactoryInterface`
* added `Context\ExecutionContextFactory`
* deprecated `ValidatorInterface`
* deprecated `Validator`
* deprecated `ValidationVisitorInterface`
* deprecated `ValidationVisitor`
* added `Validator\ValidatorInterface`
* added `Validator\RecursiveValidator`
* added `Validator\ContextualValidatorInterface`
* added `Validator\RecursiveContextualValidator`
* added `Violation\ConstraintViolationBuilderInterface`
* added `Violation\ConstraintViolationBuilder`
* added `ConstraintViolation::getParameters()`
* added `ConstraintViolation::getPlural()`
* added `Constraints\Traverse`
* deprecated `$deep` property in `Constraints\Valid`
* added `ValidatorBuilderInterface::setApiVersion()`
* added `Validation::API_VERSION_2_4`
* added `Validation::API_VERSION_2_5`
* added `Exception\OutOfBoundsException`
* added `Exception\UnsupportedMetadataException`
* made `Exception\ValidatorException` extend `Exception\RuntimeException`
* added `Util\PropertyPath`
* made the PropertyAccess component an optional dependency
* deprecated `ValidatorBuilder::setPropertyAccessor()`
* deprecated `validate` and `validateValue` on `Validator\Context\ExecutionContext` use `getValidator()` together with `inContext()` instead
2.4.0
-----
* added a constraint the uses the expression language
* added `minRatio`, `maxRatio`, `allowSquare`, `allowLandscape`, and `allowPortrait` to Image validator
2.3.29
------
* fixed compatibility with PHP7 and up by introducing new constraints (IsNull, IsTrue, IsFalse) and related validators (IsNullValidator, IsTrueValidator, IsFalseValidator)
2.3.0
-----
* added the ISBN, ISSN, and IBAN validators
* copied the constraints `Optional` and `Required` to the
`Symfony\Component\Validator\Constraints\` namespace and deprecated the original
classes.
* added comparison validators (EqualTo, NotEqualTo, LessThan, LessThanOrEqualTo, GreaterThan, GreaterThanOrEqualTo, IdenticalTo, NotIdenticalTo)
2.2.0
-----
* added a CardScheme validator
* added a Luhn validator
* moved @api-tags from `Validator` to `ValidatorInterface`
* moved @api-tags from `ConstraintViolation` to the new `ConstraintViolationInterface`
* moved @api-tags from `ConstraintViolationList` to the new `ConstraintViolationListInterface`
* moved @api-tags from `ExecutionContext` to the new `ExecutionContextInterface`
* [BC BREAK] `ConstraintValidatorInterface::initialize` is now type hinted against `ExecutionContextInterface` instead of `ExecutionContext`
* [BC BREAK] changed the visibility of the properties in `Validator` from protected to private
* deprecated `ClassMetadataFactoryInterface` in favor of the new `MetadataFactoryInterface`
* deprecated `ClassMetadataFactory::getClassMetadata` in favor of `getMetadataFor`
* created `MetadataInterface`, `PropertyMetadataInterface`, `ClassBasedInterface` and `PropertyMetadataContainerInterface`
* deprecated `GraphWalker` in favor of the new `ValidationVisitorInterface`
* deprecated `ExecutionContext::addViolationAtPath`
* deprecated `ExecutionContext::addViolationAtSubPath` in favor of `ExecutionContextInterface::addViolationAt`
* deprecated `ExecutionContext::getCurrentClass` in favor of `ExecutionContextInterface::getClassName`
* deprecated `ExecutionContext::getCurrentProperty` in favor of `ExecutionContextInterface::getPropertyName`
* deprecated `ExecutionContext::getCurrentValue` in favor of `ExecutionContextInterface::getValue`
* deprecated `ExecutionContext::getGraphWalker` in favor of `ExecutionContextInterface::validate` and `ExecutionContextInterface::validateValue`
* improved `ValidatorInterface::validateValue` to accept arrays of constraints
* changed `ValidatorInterface::getMetadataFactory` to return a `MetadataFactoryInterface` instead of a `ClassMetadataFactoryInterface`
* removed `ClassMetadataFactoryInterface` type hint from `ValidatorBuilderInterface::setMetadataFactory`.
As of Symfony 2.3, this method will be typed against `MetadataFactoryInterface` instead.
* [BC BREAK] the switches `traverse` and `deep` in the `Valid` constraint and in `GraphWalker::walkReference`
are ignored for arrays now. Arrays are always traversed recursively.
* added dependency to Translation component
* violation messages are now translated with a TranslatorInterface implementation
* [BC BREAK] inserted argument `$message` in the constructor of `ConstraintViolation`
* [BC BREAK] inserted arguments `$translator` and `$translationDomain` in the constructor of `ExecutionContext`
* [BC BREAK] inserted arguments `$translator` and `$translationDomain` in the constructor of `GraphWalker`
* [BC BREAK] inserted arguments `$translator` and `$translationDomain` in the constructor of `ValidationVisitor`
* [BC BREAK] inserted arguments `$translator` and `$translationDomain` in the constructor of `Validator`
* [BC BREAK] added `setTranslator()` and `setTranslationDomain()` to `ValidatorBuilderInterface`
* improved the Validator to support pluralized messages by default
* [BC BREAK] changed the source of all pluralized messages in the translation files to the pluralized version
* added ExceptionInterface, BadMethodCallException and InvalidArgumentException
2.1.0
-----
* added support for `ctype_*` assertions in `TypeValidator`
* improved the ImageValidator with min width, max width, min height, and max height constraints
* added support for MIME with wildcard in FileValidator
* changed Collection validator to add "missing" and "extra" errors to
individual fields
* changed default value for `extraFieldsMessage` and `missingFieldsMessage`
in Collection constraint
* made ExecutionContext immutable
* deprecated Constraint methods `setMessage`, `getMessageTemplate` and
`getMessageParameters`
* added support for dynamic group sequences with the GroupSequenceProvider pattern
* [BC BREAK] ConstraintValidatorInterface method `isValid` has been renamed to
`validate`, its return value was dropped. ConstraintValidator still contains
`isValid` for BC
* [BC BREAK] collections in fields annotated with `Valid` are not traversed
recursively anymore by default. `Valid` contains a new property `deep`
which enables the BC behavior.
* added Count constraint
* added Length constraint
* added Range constraint
* deprecated the Min and Max constraints
* deprecated the MinLength and MaxLength constraints
* added Validation and ValidatorBuilderInterface
* deprecated ValidatorContext, ValidatorContextInterface and ValidatorFactory

View File

@@ -0,0 +1,253 @@
<?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\Component\Validator\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Dumper;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Finder\Exception\DirectoryNotFoundException;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Mapping\AutoMappingStrategy;
use Symfony\Component\Validator\Mapping\CascadingStrategy;
use Symfony\Component\Validator\Mapping\ClassMetadataInterface;
use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
use Symfony\Component\Validator\Mapping\GenericMetadata;
use Symfony\Component\Validator\Mapping\TraversalStrategy;
/**
* A console command to debug Validators information.
*
* @author Loïc Frémont <lc.fremont@gmail.com>
*/
#[AsCommand(name: 'debug:validator', description: 'Display validation constraints for classes')]
class DebugCommand extends Command
{
private MetadataFactoryInterface $validator;
public function __construct(MetadataFactoryInterface $validator)
{
parent::__construct();
$this->validator = $validator;
}
/**
* @return void
*/
protected function configure()
{
$this
->addArgument('class', InputArgument::REQUIRED, 'A fully qualified class name or a path')
->addOption('show-all', null, InputOption::VALUE_NONE, 'Show all classes even if they have no validation constraints')
->setHelp(<<<'EOF'
The <info>%command.name% 'App\Entity\Dummy'</info> command dumps the validators for the dummy class.
The <info>%command.name% src/</info> command dumps the validators for the `src` directory.
EOF
)
;
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$class = $input->getArgument('class');
if (class_exists($class)) {
$this->dumpValidatorsForClass($input, $output, $class);
return 0;
}
try {
foreach ($this->getResourcesByPath($class) as $class) {
$this->dumpValidatorsForClass($input, $output, $class);
}
} catch (DirectoryNotFoundException) {
$io = new SymfonyStyle($input, $output);
$io->error(\sprintf('Neither class nor path were found with "%s" argument.', $input->getArgument('class')));
return 1;
}
return 0;
}
private function dumpValidatorsForClass(InputInterface $input, OutputInterface $output, string $class): void
{
$io = new SymfonyStyle($input, $output);
$title = \sprintf('<info>%s</info>', $class);
$rows = [];
$dump = new Dumper($output);
/** @var ClassMetadataInterface $classMetadata */
$classMetadata = $this->validator->getMetadataFor($class);
foreach ($this->getClassConstraintsData($classMetadata) as $data) {
$rows[] = [
'-',
$data['class'],
implode(', ', $data['groups']),
$dump($data['options']),
];
}
foreach ($this->getConstrainedPropertiesData($classMetadata) as $propertyName => $constraintsData) {
foreach ($constraintsData as $data) {
$rows[] = [
$propertyName,
$data['class'],
implode(', ', $data['groups']),
$dump($data['options']),
];
}
}
if (!$rows) {
if (false === $input->getOption('show-all')) {
return;
}
$io->section($title);
$io->text('No validators were found for this class.');
return;
}
$io->section($title);
$table = new Table($output);
$table->setHeaders(['Property', 'Name', 'Groups', 'Options']);
$table->setRows($rows);
$table->setColumnMaxWidth(3, 80);
$table->render();
}
private function getClassConstraintsData(ClassMetadataInterface $classMetadata): iterable
{
foreach ($classMetadata->getConstraints() as $constraint) {
yield [
'class' => $constraint::class,
'groups' => $constraint->groups,
'options' => $this->getConstraintOptions($constraint),
];
}
}
private function getConstrainedPropertiesData(ClassMetadataInterface $classMetadata): array
{
$data = [];
foreach ($classMetadata->getConstrainedProperties() as $constrainedProperty) {
$data[$constrainedProperty] = $this->getPropertyData($classMetadata, $constrainedProperty);
}
return $data;
}
private function getPropertyData(ClassMetadataInterface $classMetadata, string $constrainedProperty): array
{
$data = [];
$propertyMetadata = $classMetadata->getPropertyMetadata($constrainedProperty);
foreach ($propertyMetadata as $metadata) {
$autoMapingStrategy = 'Not supported';
if ($metadata instanceof GenericMetadata) {
$autoMapingStrategy = match ($metadata->getAutoMappingStrategy()) {
AutoMappingStrategy::ENABLED => 'Enabled',
AutoMappingStrategy::DISABLED => 'Disabled',
AutoMappingStrategy::NONE => 'None',
};
}
$traversalStrategy = 'None';
if (TraversalStrategy::TRAVERSE === $metadata->getTraversalStrategy()) {
$traversalStrategy = 'Traverse';
}
if (TraversalStrategy::IMPLICIT === $metadata->getTraversalStrategy()) {
$traversalStrategy = 'Implicit';
}
$data[] = [
'class' => 'property options',
'groups' => [],
'options' => [
'cascadeStrategy' => CascadingStrategy::CASCADE === $metadata->getCascadingStrategy() ? 'Cascade' : 'None',
'autoMappingStrategy' => $autoMapingStrategy,
'traversalStrategy' => $traversalStrategy,
],
];
foreach ($metadata->getConstraints() as $constraint) {
$data[] = [
'class' => $constraint::class,
'groups' => $constraint->groups,
'options' => $this->getConstraintOptions($constraint),
];
}
}
return $data;
}
private function getConstraintOptions(Constraint $constraint): array
{
$options = [];
foreach (array_keys(get_object_vars($constraint)) as $propertyName) {
// Groups are dumped on a specific column.
if ('groups' === $propertyName) {
continue;
}
$options[$propertyName] = $constraint->$propertyName;
}
ksort($options);
return $options;
}
private function getResourcesByPath(string $path): array
{
$finder = new Finder();
$finder->files()->in($path)->name('*.php')->sortByName(true);
$classes = [];
foreach ($finder as $file) {
$fileContent = file_get_contents($file->getRealPath());
preg_match('/namespace (.+);/', $fileContent, $matches);
$namespace = $matches[1] ?? null;
if (!preg_match('/class +([^{ ]+)/', $fileContent, $matches)) {
// no class found
continue;
}
$className = trim($matches[1]);
if (null !== $namespace) {
$classes[] = $namespace.'\\'.$className;
} else {
$classes[] = $className;
}
}
return $classes;
}
}

View File

@@ -0,0 +1,321 @@
<?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\Component\Validator;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Exception\InvalidArgumentException;
use Symfony\Component\Validator\Exception\InvalidOptionsException;
use Symfony\Component\Validator\Exception\MissingOptionsException;
/**
* Contains the properties of a constraint definition.
*
* A constraint can be defined on a class, a property or a getter method.
* The Constraint class encapsulates all the configuration required for
* validating this class, property or getter result successfully.
*
* Constraint instances are immutable and serializable.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
abstract class Constraint
{
/**
* The name of the group given to all constraints with no explicit group.
*/
public const DEFAULT_GROUP = 'Default';
/**
* Marks a constraint that can be put onto classes.
*/
public const CLASS_CONSTRAINT = 'class';
/**
* Marks a constraint that can be put onto properties.
*/
public const PROPERTY_CONSTRAINT = 'property';
/**
* Maps error codes to the names of their constants.
*
* @var array<string, string>
*/
protected const ERROR_NAMES = [];
/**
* @deprecated since Symfony 6.1, use protected const ERROR_NAMES instead
*/
protected static $errorNames = [];
/**
* Domain-specific data attached to a constraint.
*
* @var mixed
*/
public $payload;
/**
* The groups that the constraint belongs to.
*
* @var string[]
*/
public $groups;
/**
* Returns the name of the given error code.
*
* @throws InvalidArgumentException If the error code does not exist
*/
public static function getErrorName(string $errorCode): string
{
if (isset(static::ERROR_NAMES[$errorCode])) {
return static::ERROR_NAMES[$errorCode];
}
if (!isset(static::$errorNames[$errorCode])) {
throw new InvalidArgumentException(\sprintf('The error code "%s" does not exist for constraint of type "%s".', $errorCode, static::class));
}
trigger_deprecation('symfony/validator', '6.1', 'The "%s::$errorNames" property is deprecated, use protected const ERROR_NAMES instead.', static::class);
return static::$errorNames[$errorCode];
}
/**
* Initializes the constraint with options.
*
* You should pass an associative array. The keys should be the names of
* existing properties in this class. The values should be the value for these
* properties.
*
* Alternatively you can override the method getDefaultOption() to return the
* name of an existing property. If no associative array is passed, this
* property is set instead.
*
* You can force that certain options are set by overriding
* getRequiredOptions() to return the names of these options. If any
* option is not set here, an exception is thrown.
*
* @param mixed $options The options (as associative array)
* or the value for the default
* option (any other type)
* @param string[] $groups An array of validation groups
* @param mixed $payload Domain-specific data attached to a constraint
*
* @throws InvalidOptionsException When you pass the names of non-existing
* options
* @throws MissingOptionsException When you don't pass any of the options
* returned by getRequiredOptions()
* @throws ConstraintDefinitionException When you don't pass an associative
* array, but getDefaultOption() returns
* null
*/
public function __construct(mixed $options = null, ?array $groups = null, mixed $payload = null)
{
unset($this->groups); // enable lazy initialization
$options = $this->normalizeOptions($options);
if (null !== $groups) {
$options['groups'] = $groups;
}
$options['payload'] = $payload ?? $options['payload'] ?? null;
foreach ($options as $name => $value) {
$this->$name = $value;
}
}
protected function normalizeOptions(mixed $options): array
{
$normalizedOptions = [];
$defaultOption = $this->getDefaultOption();
$invalidOptions = [];
$missingOptions = array_flip((array) $this->getRequiredOptions());
$knownOptions = get_class_vars(static::class);
if (\is_array($options) && isset($options['value']) && !property_exists($this, 'value')) {
if (null === $defaultOption) {
throw new ConstraintDefinitionException(\sprintf('No default option is configured for constraint "%s".', static::class));
}
$options[$defaultOption] = $options['value'];
unset($options['value']);
}
if (\is_array($options)) {
reset($options);
}
if ($options && \is_array($options) && \is_string(key($options))) {
foreach ($options as $option => $value) {
if (\array_key_exists($option, $knownOptions)) {
$normalizedOptions[$option] = $value;
unset($missingOptions[$option]);
} else {
$invalidOptions[] = $option;
}
}
} elseif (null !== $options && !(\is_array($options) && 0 === \count($options))) {
if (null === $defaultOption) {
throw new ConstraintDefinitionException(\sprintf('No default option is configured for constraint "%s".', static::class));
}
if (\array_key_exists($defaultOption, $knownOptions)) {
$normalizedOptions[$defaultOption] = $options;
unset($missingOptions[$defaultOption]);
} else {
$invalidOptions[] = $defaultOption;
}
}
if (\count($invalidOptions) > 0) {
throw new InvalidOptionsException(\sprintf('The options "%s" do not exist in constraint "%s".', implode('", "', $invalidOptions), static::class), $invalidOptions);
}
if (\count($missingOptions) > 0) {
throw new MissingOptionsException(\sprintf('The options "%s" must be set for constraint "%s".', implode('", "', array_keys($missingOptions)), static::class), array_keys($missingOptions));
}
return $normalizedOptions;
}
/**
* Sets the value of a lazily initialized option.
*
* Corresponding properties are added to the object on first access. Hence
* this method will be called at most once per constraint instance and
* option name.
*
* @return void
*
* @throws InvalidOptionsException If an invalid option name is given
*/
public function __set(string $option, mixed $value)
{
if ('groups' === $option) {
$this->groups = (array) $value;
return;
}
throw new InvalidOptionsException(\sprintf('The option "%s" does not exist in constraint "%s".', $option, static::class), [$option]);
}
/**
* Returns the value of a lazily initialized option.
*
* Corresponding properties are added to the object on first access. Hence
* this method will be called at most once per constraint instance and
* option name.
*
* @throws InvalidOptionsException If an invalid option name is given
*/
public function __get(string $option): mixed
{
if ('groups' === $option) {
$this->groups = [self::DEFAULT_GROUP];
return $this->groups;
}
throw new InvalidOptionsException(\sprintf('The option "%s" does not exist in constraint "%s".', $option, static::class), [$option]);
}
public function __isset(string $option): bool
{
return 'groups' === $option;
}
/**
* Adds the given group if this constraint is in the Default group.
*
* @return void
*/
public function addImplicitGroupName(string $group)
{
if (null === $this->groups && \array_key_exists('groups', (array) $this)) {
throw new \LogicException(\sprintf('"%s::$groups" is set to null. Did you forget to call "%s::__construct()"?', static::class, self::class));
}
if (\in_array(self::DEFAULT_GROUP, $this->groups) && !\in_array($group, $this->groups)) {
$this->groups[] = $group;
}
}
/**
* Returns the name of the default option.
*
* Override this method to define a default option.
*
* @return string|null
*
* @see __construct()
*/
public function getDefaultOption()
{
return null;
}
/**
* Returns the name of the required options.
*
* Override this method if you want to define required options.
*
* @return string[]
*
* @see __construct()
*/
public function getRequiredOptions()
{
return [];
}
/**
* Returns the name of the class that validates this constraint.
*
* By default, this is the fully qualified name of the constraint class
* suffixed with "Validator". You can override this method to change that
* behavior.
*
* @return string
*/
public function validatedBy()
{
return static::class.'Validator';
}
/**
* Returns whether the constraint can be put onto classes, properties or
* both.
*
* This method should return one or more of the constants
* Constraint::CLASS_CONSTRAINT and Constraint::PROPERTY_CONSTRAINT.
*
* @return string|string[] One or more constant values
*/
public function getTargets()
{
return self::PROPERTY_CONSTRAINT;
}
/**
* Optimizes the serialized value to minimize storage space.
*
* @internal
*/
public function __sleep(): array
{
// Initialize "groups" option if it is not set
$this->groups;
return array_keys(get_object_vars($this));
}
}

View File

@@ -0,0 +1,157 @@
<?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\Component\Validator;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
* Base class for constraint validators.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
abstract class ConstraintValidator implements ConstraintValidatorInterface
{
/**
* Whether to format {@link \DateTime} objects, either with the {@link \IntlDateFormatter}
* (if it is available) or as RFC-3339 dates ("Y-m-d H:i:s").
*/
public const PRETTY_DATE = 1;
/**
* Whether to cast objects with a "__toString()" method to strings.
*/
public const OBJECT_TO_STRING = 2;
/**
* @var ExecutionContextInterface
*/
protected $context;
/**
* @return void
*/
public function initialize(ExecutionContextInterface $context)
{
$this->context = $context;
}
/**
* Returns a string representation of the type of the value.
*
* This method should be used if you pass the type of a value as
* message parameter to a constraint violation. Note that such
* parameters should usually not be included in messages aimed at
* non-technical people.
*/
protected function formatTypeOf(mixed $value): string
{
return get_debug_type($value);
}
/**
* Returns a string representation of the value.
*
* This method returns the equivalent PHP tokens for most scalar types
* (i.e. "false" for false, "1" for 1 etc.). Strings are always wrapped
* in double quotes ("). Objects, arrays and resources are formatted as
* "object", "array" and "resource". If the $format bitmask contains
* the PRETTY_DATE bit, then {@link \DateTime} objects will be formatted
* with the {@link \IntlDateFormatter}. If it is not available, they will be
* formatted as RFC-3339 dates ("Y-m-d H:i:s").
*
* Be careful when passing message parameters to a constraint violation
* that (may) contain objects, arrays or resources. These parameters
* should only be displayed for technical users. Non-technical users
* won't know what an "object", "array" or "resource" is and will be
* confused by the violation message.
*
* @param int $format A bitwise combination of the format constants in this class
*/
protected function formatValue(mixed $value, int $format = 0): string
{
if (($format & self::PRETTY_DATE) && $value instanceof \DateTimeInterface) {
if (class_exists(\IntlDateFormatter::class)) {
$formatter = new \IntlDateFormatter(\Locale::getDefault(), \IntlDateFormatter::MEDIUM, \IntlDateFormatter::SHORT, 'UTC');
return $formatter->format(new \DateTimeImmutable(
$value->format('Y-m-d H:i:s.u'),
new \DateTimeZone('UTC')
));
}
return $value->format('Y-m-d H:i:s');
}
if ($value instanceof \UnitEnum) {
return $value->name;
}
if (\is_object($value)) {
if (($format & self::OBJECT_TO_STRING) && $value instanceof \Stringable) {
return $value->__toString();
}
return 'object';
}
if (\is_array($value)) {
return 'array';
}
if (\is_string($value)) {
return '"'.$value.'"';
}
if (\is_resource($value)) {
return 'resource';
}
if (null === $value) {
return 'null';
}
if (false === $value) {
return 'false';
}
if (true === $value) {
return 'true';
}
if (is_nan($value)) {
return 'NAN';
}
return (string) $value;
}
/**
* Returns a string representation of a list of values.
*
* Each of the values is converted to a string using
* {@link formatValue()}. The values are then concatenated with commas.
*
* @param array $values A list of values
* @param int $format A bitwise combination of the format
* constants in this class
*
* @see formatValue()
*/
protected function formatValues(array $values, int $format = 0): string
{
foreach ($values as $key => $value) {
$values[$key] = $this->formatValue($value, $format);
}
return implode(', ', $values);
}
}

View File

@@ -0,0 +1,42 @@
<?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\Component\Validator;
use Symfony\Component\Validator\Constraints\ExpressionValidator;
/**
* Default implementation of the ConstraintValidatorFactoryInterface.
*
* This enforces the convention that the validatedBy() method on any
* Constraint will return the class name of the ConstraintValidator that
* should validate the Constraint.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class ConstraintValidatorFactory implements ConstraintValidatorFactoryInterface
{
protected $validators = [];
public function __construct(array $validators = [])
{
$this->validators = $validators;
}
public function getInstance(Constraint $constraint): ConstraintValidatorInterface
{
if ('validator.expression' === $name = $class = $constraint->validatedBy()) {
$class = ExpressionValidator::class;
}
return $this->validators[$name] ??= new $class();
}
}

View File

@@ -0,0 +1,25 @@
<?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\Component\Validator;
/**
* Specifies an object able to return the correct ConstraintValidatorInterface
* instance given a Constraint object.
*/
interface ConstraintValidatorFactoryInterface
{
/**
* Given a Constraint, this returns the ConstraintValidatorInterface
* object that should be used to verify its validity.
*/
public function getInstance(Constraint $constraint): ConstraintValidatorInterface;
}

View File

@@ -0,0 +1,34 @@
<?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\Component\Validator;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface ConstraintValidatorInterface
{
/**
* Initializes the constraint validator.
*
* @return void
*/
public function initialize(ExecutionContextInterface $context);
/**
* Checks if the passed value is valid.
*
* @return void
*/
public function validate(mixed $value, Constraint $constraint);
}

View File

@@ -0,0 +1,144 @@
<?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\Component\Validator;
/**
* Default implementation of {@ConstraintViolationInterface}.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class ConstraintViolation implements ConstraintViolationInterface
{
private string|\Stringable $message;
private ?string $messageTemplate;
private array $parameters;
private ?int $plural;
private mixed $root;
private ?string $propertyPath;
private mixed $invalidValue;
private ?Constraint $constraint;
private ?string $code;
private mixed $cause;
/**
* Creates a new constraint violation.
*
* @param string|\Stringable $message The violation message as a string or a stringable object
* @param string|null $messageTemplate The raw violation message
* @param array $parameters The parameters to substitute in the
* raw violation message
* @param mixed $root The value originally passed to the
* validator
* @param string|null $propertyPath The property path from the root
* value to the invalid value
* @param mixed $invalidValue The invalid value that caused this
* violation
* @param int|null $plural The number for determining the plural
* form when translating the message
* @param string|null $code The error code of the violation
* @param Constraint|null $constraint The constraint whose validation
* caused the violation
* @param mixed $cause The cause of the violation
*/
public function __construct(string|\Stringable $message, ?string $messageTemplate, array $parameters, mixed $root, ?string $propertyPath, mixed $invalidValue, ?int $plural = null, ?string $code = null, ?Constraint $constraint = null, mixed $cause = null)
{
$this->message = $message;
$this->messageTemplate = $messageTemplate;
$this->parameters = $parameters;
$this->plural = $plural;
$this->root = $root;
$this->propertyPath = $propertyPath;
$this->invalidValue = $invalidValue;
$this->constraint = $constraint;
$this->code = $code;
$this->cause = $cause;
}
public function __toString(): string
{
if (\is_object($this->root)) {
$class = 'Object('.$this->root::class.')';
} elseif (\is_array($this->root)) {
$class = 'Array';
} else {
$class = (string) $this->root;
}
$propertyPath = (string) $this->propertyPath;
if ('' !== $propertyPath && '[' !== $propertyPath[0] && '' !== $class) {
$class .= '.';
}
if (null !== ($code = $this->code) && '' !== $code) {
$code = ' (code '.$code.')';
}
return $class.$propertyPath.":\n ".$this->getMessage().$code;
}
public function getMessageTemplate(): string
{
return (string) $this->messageTemplate;
}
public function getParameters(): array
{
return $this->parameters;
}
public function getPlural(): ?int
{
return $this->plural;
}
public function getMessage(): string|\Stringable
{
return $this->message;
}
public function getRoot(): mixed
{
return $this->root;
}
public function getPropertyPath(): string
{
return (string) $this->propertyPath;
}
public function getInvalidValue(): mixed
{
return $this->invalidValue;
}
/**
* Returns the constraint whose validation caused the violation.
*/
public function getConstraint(): ?Constraint
{
return $this->constraint;
}
/**
* Returns the cause of the violation.
*/
public function getCause(): mixed
{
return $this->cause;
}
public function getCode(): ?string
{
return $this->code;
}
}

View File

@@ -0,0 +1,116 @@
<?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\Component\Validator;
/**
* A violation of a constraint that happened during validation.
*
* For each constraint that fails during validation one or more violations are
* created. The violations store the violation message, the path to the failing
* element in the validation graph and the root element that was originally
* passed to the validator. For example, take the following graph:
*
* (Person)---(firstName: string)
* \
* (address: Address)---(street: string)
*
* If the <tt>Person</tt> object is validated and validation fails for the
* "firstName" property, the generated violation has the <tt>Person</tt>
* instance as root and the property path "firstName". If validation fails
* for the "street" property of the related <tt>Address</tt> instance, the root
* element is still the person, but the property path is "address.street".
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @method Constraint|null getConstraint() Returns the constraint whose validation caused the violation. Not implementing it is deprecated since Symfony 6.3.
* @method mixed getCause() Returns the cause of the violation. Not implementing it is deprecated since Symfony 6.2.
* @method string __toString() Converts the violation into a string for debugging purposes. Not implementing it is deprecated since Symfony 6.1.
*/
interface ConstraintViolationInterface
{
/**
* Returns the violation message.
*/
public function getMessage(): string|\Stringable;
/**
* Returns the raw violation message.
*
* The raw violation message contains placeholders for the parameters
* returned by {@link getParameters}. Typically you'll pass the
* message template and parameters to a translation engine.
*/
public function getMessageTemplate(): string;
/**
* Returns the parameters to be inserted into the raw violation message.
*
* @return array a possibly empty list of parameters indexed by the names
* that appear in the message template
*
* @see getMessageTemplate()
*/
public function getParameters(): array;
/**
* Returns a number for pluralizing the violation message.
*
* For example, the message template could have different translation based
* on a parameter "choices":
*
* <ul>
* <li>Please select exactly one entry. (choices=1)</li>
* <li>Please select two entries. (choices=2)</li>
* </ul>
*
* This method returns the value of the parameter for choosing the right
* pluralization form (in this case "choices").
*/
public function getPlural(): ?int;
/**
* Returns the root element of the validation.
*
* @return mixed The value that was passed originally to the validator when
* the validation was started. Because the validator traverses
* the object graph, the value at which the violation occurs
* is not necessarily the value that was originally validated.
*/
public function getRoot(): mixed;
/**
* Returns the property path from the root element to the violation.
*
* @return string The property path indicates how the validator reached
* the invalid value from the root element. If the root
* element is a <tt>Person</tt> instance with a property
* "address" that contains an <tt>Address</tt> instance
* with an invalid property "street", the generated property
* path is "address.street". Property access is denoted by
* dots, while array access is denoted by square brackets,
* for example "addresses[1].street".
*/
public function getPropertyPath(): string;
/**
* Returns the value that caused the violation.
*
* @return mixed the invalid value that caused the validated constraint to
* fail
*/
public function getInvalidValue(): mixed;
/**
* Returns a machine-digestible error code for the violation.
*/
public function getCode(): ?string;
}

View File

@@ -0,0 +1,163 @@
<?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\Component\Validator;
use Symfony\Component\Validator\Exception\OutOfBoundsException;
/**
* Default implementation of {@ConstraintViolationListInterface}.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @implements \IteratorAggregate<int, ConstraintViolationInterface>
*/
class ConstraintViolationList implements \IteratorAggregate, ConstraintViolationListInterface
{
/**
* @var list<ConstraintViolationInterface>
*/
private array $violations = [];
/**
* Creates a new constraint violation list.
*
* @param iterable<mixed, ConstraintViolationInterface> $violations The constraint violations to add to the list
*/
public function __construct(iterable $violations = [])
{
foreach ($violations as $violation) {
$this->add($violation);
}
}
public static function createFromMessage(string $message): self
{
$self = new self();
$self->add(new ConstraintViolation($message, '', [], null, '', null));
return $self;
}
public function __toString(): string
{
$string = '';
foreach ($this->violations as $violation) {
$string .= $violation."\n";
}
return $string;
}
/**
* @return void
*/
public function add(ConstraintViolationInterface $violation)
{
$this->violations[] = $violation;
}
/**
* @return void
*/
public function addAll(ConstraintViolationListInterface $otherList)
{
foreach ($otherList as $violation) {
$this->violations[] = $violation;
}
}
public function get(int $offset): ConstraintViolationInterface
{
if (!isset($this->violations[$offset])) {
throw new OutOfBoundsException(\sprintf('The offset "%s" does not exist.', $offset));
}
return $this->violations[$offset];
}
public function has(int $offset): bool
{
return isset($this->violations[$offset]);
}
/**
* @return void
*/
public function set(int $offset, ConstraintViolationInterface $violation)
{
$this->violations[$offset] = $violation;
}
/**
* @return void
*/
public function remove(int $offset)
{
unset($this->violations[$offset]);
}
/**
* @return \ArrayIterator<int, ConstraintViolationInterface>
*/
public function getIterator(): \ArrayIterator
{
return new \ArrayIterator($this->violations);
}
public function count(): int
{
return \count($this->violations);
}
public function offsetExists(mixed $offset): bool
{
return $this->has($offset);
}
public function offsetGet(mixed $offset): ConstraintViolationInterface
{
return $this->get($offset);
}
public function offsetSet(mixed $offset, mixed $violation): void
{
if (null === $offset) {
$this->add($violation);
} else {
$this->set($offset, $violation);
}
}
public function offsetUnset(mixed $offset): void
{
$this->remove($offset);
}
/**
* Creates iterator for errors with specific codes.
*
* @param string|string[] $codes The codes to find
*/
public function findByCodes(string|array $codes): static
{
$codes = (array) $codes;
$violations = [];
foreach ($this as $violation) {
if (\in_array($violation->getCode(), $codes, true)) {
$violations[] = $violation;
}
}
return new static($violations);
}
}

View File

@@ -0,0 +1,75 @@
<?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\Component\Validator;
use Symfony\Component\Validator\Exception\OutOfBoundsException;
/**
* A list of constraint violations.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @extends \ArrayAccess<int, ConstraintViolationInterface>
* @extends \Traversable<int, ConstraintViolationInterface>
*
* @method string __toString() Converts the violation into a string for debugging purposes. Not implementing it is deprecated since Symfony 6.1.
*/
interface ConstraintViolationListInterface extends \Traversable, \Countable, \ArrayAccess
{
/**
* Adds a constraint violation to this list.
*
* @return void
*/
public function add(ConstraintViolationInterface $violation);
/**
* Merges an existing violation list into this list.
*
* @return void
*/
public function addAll(self $otherList);
/**
* Returns the violation at a given offset.
*
* @param int $offset The offset of the violation
*
* @throws OutOfBoundsException if the offset does not exist
*/
public function get(int $offset): ConstraintViolationInterface;
/**
* Returns whether the given offset exists.
*
* @param int $offset The violation offset
*/
public function has(int $offset): bool;
/**
* Sets a violation at a given offset.
*
* @param int $offset The violation offset
*
* @return void
*/
public function set(int $offset, ConstraintViolationInterface $violation);
/**
* Removes a violation at a given offset.
*
* @param int $offset The offset to remove
*
* @return void
*/
public function remove(int $offset);
}

View File

@@ -0,0 +1,61 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Exception\LogicException;
/**
* Used for the comparison of values.
*
* @author Daniel Holmes <daniel@danielholmes.org>
* @author Bernhard Schussek <bschussek@gmail.com>
*/
abstract class AbstractComparison extends Constraint
{
public $message;
public $value;
public $propertyPath;
public function __construct(mixed $value = null, ?string $propertyPath = null, ?string $message = null, ?array $groups = null, mixed $payload = null, array $options = [])
{
if (\is_array($value)) {
$options = array_merge($value, $options);
} elseif (null !== $value) {
$options['value'] = $value;
}
parent::__construct($options, $groups, $payload);
$this->message = $message ?? $this->message;
$this->propertyPath = $propertyPath ?? $this->propertyPath;
if (null === $this->value && null === $this->propertyPath) {
throw new ConstraintDefinitionException(\sprintf('The "%s" constraint requires either the "value" or "propertyPath" option to be set.', static::class));
}
if (null !== $this->value && null !== $this->propertyPath) {
throw new ConstraintDefinitionException(\sprintf('The "%s" constraint requires only one of the "value" or "propertyPath" options to be set, not both.', static::class));
}
if (null !== $this->propertyPath && !class_exists(PropertyAccess::class)) {
throw new LogicException(\sprintf('The "%s" constraint requires the Symfony PropertyAccess component to use the "propertyPath" option. Try running "composer require symfony/property-access".', static::class));
}
}
public function getDefaultOption(): ?string
{
return 'value';
}
}

View File

@@ -0,0 +1,110 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
/**
* Provides a base class for the validation of property comparisons.
*
* @author Daniel Holmes <daniel@danielholmes.org>
* @author Bernhard Schussek <bschussek@gmail.com>
*/
abstract class AbstractComparisonValidator extends ConstraintValidator
{
private ?PropertyAccessorInterface $propertyAccessor;
public function __construct(?PropertyAccessorInterface $propertyAccessor = null)
{
$this->propertyAccessor = $propertyAccessor;
}
/**
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof AbstractComparison) {
throw new UnexpectedTypeException($constraint, AbstractComparison::class);
}
if (null === $value) {
return;
}
if ($path = $constraint->propertyPath) {
if (null === $object = $this->context->getObject()) {
return;
}
try {
$comparedValue = $this->getPropertyAccessor()->getValue($object, $path);
} catch (NoSuchPropertyException $e) {
throw new ConstraintDefinitionException(\sprintf('Invalid property path "%s" provided to "%s" constraint: ', $path, get_debug_type($constraint)).$e->getMessage(), 0, $e);
} catch (UninitializedPropertyException) {
$comparedValue = null;
}
} else {
$comparedValue = $constraint->value;
}
// Convert strings to date-time objects if comparing to another date-time object
// This allows to compare with any date/time value supported by date-time constructors:
// https://php.net/datetime.formats
if (\is_string($comparedValue) && $value instanceof \DateTimeInterface) {
try {
$comparedValue = new $value($comparedValue);
} catch (\Exception) {
throw new ConstraintDefinitionException(\sprintf('The compared value "%s" could not be converted to a "%s" instance in the "%s" constraint.', $comparedValue, get_debug_type($value), get_debug_type($constraint)));
}
}
if (!$this->compareValues($value, $comparedValue)) {
$violationBuilder = $this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value, self::OBJECT_TO_STRING | self::PRETTY_DATE))
->setParameter('{{ compared_value }}', $this->formatValue($comparedValue, self::OBJECT_TO_STRING | self::PRETTY_DATE))
->setParameter('{{ compared_value_type }}', $this->formatTypeOf($comparedValue))
->setCode($this->getErrorCode());
if (null !== $path) {
$violationBuilder->setParameter('{{ compared_value_path }}', $path);
}
$violationBuilder->addViolation();
}
}
private function getPropertyAccessor(): PropertyAccessorInterface
{
return $this->propertyAccessor ??= PropertyAccess::createPropertyAccessor();
}
/**
* Compares the two given values to find if their relationship is valid.
*/
abstract protected function compareValues(mixed $value1, mixed $value2): bool;
/**
* Returns the error code used if the comparison fails.
*/
protected function getErrorCode(): ?string
{
return null;
}
}

View File

@@ -0,0 +1,44 @@
<?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\Component\Validator\Constraints;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class All extends Composite
{
public $constraints = [];
public function __construct(mixed $constraints = null, ?array $groups = null, mixed $payload = null)
{
parent::__construct($constraints ?? [], $groups, $payload);
}
public function getDefaultOption(): ?string
{
return 'constraints';
}
public function getRequiredOptions(): array
{
return ['constraints'];
}
protected function getCompositeOption(): string
{
return 'constraints';
}
}

View File

@@ -0,0 +1,49 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class AllValidator extends ConstraintValidator
{
/**
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof All) {
throw new UnexpectedTypeException($constraint, All::class);
}
if (null === $value) {
return;
}
if (!\is_array($value) && !$value instanceof \Traversable) {
throw new UnexpectedValueException($value, 'iterable');
}
$context = $this->context;
$validator = $context->getValidator()->inContext($context);
foreach ($value as $key => $element) {
$validator->atPath('['.$key.']')->validate($element, $constraint->constraints);
}
}
}

View File

@@ -0,0 +1,62 @@
<?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\Component\Validator\Constraints;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Przemysław Bogusz <przemyslaw.bogusz@tubotax.pl>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class AtLeastOneOf extends Composite
{
public const AT_LEAST_ONE_OF_ERROR = 'f27e6d6c-261a-4056-b391-6673a623531c';
protected const ERROR_NAMES = [
self::AT_LEAST_ONE_OF_ERROR => 'AT_LEAST_ONE_OF_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $constraints = [];
public $message = 'This value should satisfy at least one of the following constraints:';
public $messageCollection = 'Each element of this collection should satisfy its own set of constraints.';
public $includeInternalMessages = true;
public function __construct(mixed $constraints = null, ?array $groups = null, mixed $payload = null, ?string $message = null, ?string $messageCollection = null, ?bool $includeInternalMessages = null)
{
parent::__construct($constraints ?? [], $groups, $payload);
$this->message = $message ?? $this->message;
$this->messageCollection = $messageCollection ?? $this->messageCollection;
$this->includeInternalMessages = $includeInternalMessages ?? $this->includeInternalMessages;
}
public function getDefaultOption(): ?string
{
return 'constraints';
}
public function getRequiredOptions(): array
{
return ['constraints'];
}
protected function getCompositeOption(): string
{
return 'constraints';
}
}

View File

@@ -0,0 +1,73 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
/**
* @author Przemysław Bogusz <przemyslaw.bogusz@tubotax.pl>
*/
class AtLeastOneOfValidator extends ConstraintValidator
{
/**
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof AtLeastOneOf) {
throw new UnexpectedTypeException($constraint, AtLeastOneOf::class);
}
$validator = $this->context->getValidator();
// Build a first violation to have the base message of the constraint translated
$baseMessageContext = clone $this->context;
$baseMessageContext->buildViolation($constraint->message)->addViolation();
$baseViolations = $baseMessageContext->getViolations();
$messages = [(string) $baseViolations->get(\count($baseViolations) - 1)->getMessage()];
foreach ($constraint->constraints as $key => $item) {
if (!\in_array($this->context->getGroup(), $item->groups, true)) {
continue;
}
$context = $this->context;
$executionContext = clone $this->context;
$executionContext->setNode($value, $this->context->getObject(), $this->context->getMetadata(), $this->context->getPropertyPath());
$violations = $validator->inContext($executionContext)->validate($value, $item, $this->context->getGroup())->getViolations();
$this->context = $context;
if (\count($this->context->getViolations()) === \count($violations)) {
return;
}
if ($constraint->includeInternalMessages) {
$message = ' ['.($key + 1).'] ';
if ($item instanceof All || $item instanceof Collection) {
$message .= $constraint->messageCollection;
} else {
$message .= $violations->get(\count($violations) - 1)->getMessage();
}
$messages[] = $message;
}
}
$this->context->buildViolation(implode('', $messages))
->setCode(AtLeastOneOf::AT_LEAST_ONE_OF_ERROR)
->addViolation()
;
}
}

View File

@@ -0,0 +1,75 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Intl\Countries;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Exception\LogicException;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Michael Hirschler <michael.vhirsch@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Bic extends Constraint
{
public const INVALID_LENGTH_ERROR = '66dad313-af0b-4214-8566-6c799be9789c';
public const INVALID_CHARACTERS_ERROR = 'f424c529-7add-4417-8f2d-4b656e4833e2';
public const INVALID_BANK_CODE_ERROR = '00559357-6170-4f29-aebd-d19330aa19cf';
public const INVALID_COUNTRY_CODE_ERROR = '1ce76f8d-3c1f-451c-9e62-fe9c3ed486ae';
public const INVALID_CASE_ERROR = '11884038-3312-4ae5-9d04-699f782130c7';
public const INVALID_IBAN_COUNTRY_CODE_ERROR = '29a2c3bb-587b-4996-b6f5-53081364cea5';
protected const ERROR_NAMES = [
self::INVALID_LENGTH_ERROR => 'INVALID_LENGTH_ERROR',
self::INVALID_CHARACTERS_ERROR => 'INVALID_CHARACTERS_ERROR',
self::INVALID_BANK_CODE_ERROR => 'INVALID_BANK_CODE_ERROR',
self::INVALID_COUNTRY_CODE_ERROR => 'INVALID_COUNTRY_CODE_ERROR',
self::INVALID_CASE_ERROR => 'INVALID_CASE_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $message = 'This is not a valid Business Identifier Code (BIC).';
public $ibanMessage = 'This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.';
public $iban;
public $ibanPropertyPath;
public function __construct(?array $options = null, ?string $message = null, ?string $iban = null, ?string $ibanPropertyPath = null, ?string $ibanMessage = null, ?array $groups = null, mixed $payload = null)
{
if (!class_exists(Countries::class)) {
throw new LogicException('The Intl component is required to use the Bic constraint. Try running "composer require symfony/intl".');
}
parent::__construct($options, $groups, $payload);
$this->message = $message ?? $this->message;
$this->ibanMessage = $ibanMessage ?? $this->ibanMessage;
$this->iban = $iban ?? $this->iban;
$this->ibanPropertyPath = $ibanPropertyPath ?? $this->ibanPropertyPath;
if (null !== $this->iban && null !== $this->ibanPropertyPath) {
throw new ConstraintDefinitionException('The "iban" and "ibanPropertyPath" options of the Iban constraint cannot be used at the same time.');
}
if (null !== $this->ibanPropertyPath && !class_exists(PropertyAccess::class)) {
throw new LogicException(\sprintf('The "symfony/property-access" component is required to use the "%s" constraint with the "ibanPropertyPath" option. Try running "composer require symfony/property-access".', self::class));
}
}
}

View File

@@ -0,0 +1,167 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Intl\Countries;
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessor;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Exception\LogicException;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
/**
* @author Michael Hirschler <michael.vhirsch@gmail.com>
*
* @see https://en.wikipedia.org/wiki/ISO_9362#Structure
*/
class BicValidator extends ConstraintValidator
{
// Reference: https://www.iban.com/structure
private const BIC_COUNTRY_TO_IBAN_COUNTRY_MAP = [
// FR includes:
'GF' => 'FR', // French Guiana
'PF' => 'FR', // French Polynesia
'TF' => 'FR', // French Southern Territories
'GP' => 'FR', // Guadeloupe
'MQ' => 'FR', // Martinique
'YT' => 'FR', // Mayotte
'NC' => 'FR', // New Caledonia
'RE' => 'FR', // Reunion
'BL' => 'FR', // Saint Barthelemy
'MF' => 'FR', // Saint Martin (French part)
'PM' => 'FR', // Saint Pierre and Miquelon
'WF' => 'FR', // Wallis and Futuna Islands
// GB includes:
'JE' => 'GB', // Jersey
'IM' => 'GB', // Isle of Man
'GG' => 'GB', // Guernsey
'VG' => 'GB', // British Virgin Islands
// FI includes:
'AX' => 'FI', // Aland Islands
// ES includes:
'IC' => 'ES', // Canary Islands
'EA' => 'ES', // Ceuta and Melilla
];
private ?PropertyAccessor $propertyAccessor;
public function __construct(?PropertyAccessor $propertyAccessor = null)
{
$this->propertyAccessor = $propertyAccessor;
}
/**
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof Bic) {
throw new UnexpectedTypeException($constraint, Bic::class);
}
if (null === $value || '' === $value) {
return;
}
if (!\is_scalar($value) && !$value instanceof \Stringable) {
throw new UnexpectedValueException($value, 'string');
}
$canonicalize = str_replace(' ', '', $value);
// the bic must be either 8 or 11 characters long
if (!\in_array(\strlen($canonicalize), [8, 11])) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(Bic::INVALID_LENGTH_ERROR)
->addViolation();
return;
}
// must contain alphanumeric values only
if (!ctype_alnum($canonicalize)) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(Bic::INVALID_CHARACTERS_ERROR)
->addViolation();
return;
}
$bicCountryCode = substr($canonicalize, 4, 2);
if (!isset(self::BIC_COUNTRY_TO_IBAN_COUNTRY_MAP[$bicCountryCode]) && !Countries::exists($bicCountryCode)) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(Bic::INVALID_COUNTRY_CODE_ERROR)
->addViolation();
return;
}
// should contain uppercase characters only
if (strtoupper($canonicalize) !== $canonicalize) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(Bic::INVALID_CASE_ERROR)
->addViolation();
return;
}
// check against an IBAN
$iban = $constraint->iban;
$path = $constraint->ibanPropertyPath;
if ($path && null !== $object = $this->context->getObject()) {
try {
$iban = $this->getPropertyAccessor()->getValue($object, $path);
} catch (NoSuchPropertyException $e) {
throw new ConstraintDefinitionException(\sprintf('Invalid property path "%s" provided to "%s" constraint: ', $path, get_debug_type($constraint)).$e->getMessage(), 0, $e);
} catch (UninitializedPropertyException) {
$iban = null;
}
}
if (!$iban) {
return;
}
$ibanCountryCode = substr($iban, 0, 2);
if (ctype_alpha($ibanCountryCode) && !$this->bicAndIbanCountriesMatch($bicCountryCode, $ibanCountryCode)) {
$this->context->buildViolation($constraint->ibanMessage)
->setParameter('{{ value }}', $this->formatValue($value))
->setParameter('{{ iban }}', $iban)
->setCode(Bic::INVALID_IBAN_COUNTRY_CODE_ERROR)
->addViolation();
}
}
private function getPropertyAccessor(): PropertyAccessor
{
if (null === $this->propertyAccessor) {
if (!class_exists(PropertyAccess::class)) {
throw new LogicException('Unable to use property path as the Symfony PropertyAccess component is not installed. Try running "composer require symfony/property-access".');
}
$this->propertyAccessor = PropertyAccess::createPropertyAccessor();
}
return $this->propertyAccessor;
}
private function bicAndIbanCountriesMatch(string $bicCountryCode, string $ibanCountryCode): bool
{
return $ibanCountryCode === $bicCountryCode || $ibanCountryCode === (self::BIC_COUNTRY_TO_IBAN_COUNTRY_MAP[$bicCountryCode] ?? null);
}
}

View File

@@ -0,0 +1,44 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Blank extends Constraint
{
public const NOT_BLANK_ERROR = '183ad2de-533d-4796-a439-6d3c3852b549';
protected const ERROR_NAMES = [
self::NOT_BLANK_ERROR => 'NOT_BLANK_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $message = 'This value should be blank.';
public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null)
{
parent::__construct($options ?? [], $groups, $payload);
$this->message = $message ?? $this->message;
}
}

View File

@@ -0,0 +1,39 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class BlankValidator extends ConstraintValidator
{
/**
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof Blank) {
throw new UnexpectedTypeException($constraint, Blank::class);
}
if ('' !== $value && null !== $value) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(Blank::NOT_BLANK_ERROR)
->addViolation();
}
}
}

View File

@@ -0,0 +1,55 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* @Annotation
* @Target({"CLASS", "PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Callback extends Constraint
{
/**
* @var string|callable
*/
public $callback;
public function __construct(array|string|callable|null $callback = null, ?array $groups = null, mixed $payload = null, array $options = [])
{
// Invocation through annotations with an array parameter only
if (\is_array($callback) && 1 === \count($callback) && isset($callback['value'])) {
$callback = $callback['value'];
}
if (!\is_array($callback) || (!isset($callback['callback']) && !isset($callback['groups']) && !isset($callback['payload']))) {
$options['callback'] = $callback;
} else {
$options = array_merge($callback, $options);
}
parent::__construct($options, $groups, $payload);
}
public function getDefaultOption(): ?string
{
return 'callback';
}
public function getTargets(): string|array
{
return [self::CLASS_CONSTRAINT, self::PROPERTY_CONSTRAINT];
}
}

View File

@@ -0,0 +1,61 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
/**
* Validator for Callback constraint.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class CallbackValidator extends ConstraintValidator
{
/**
* @return void
*/
public function validate(mixed $object, Constraint $constraint)
{
if (!$constraint instanceof Callback) {
throw new UnexpectedTypeException($constraint, Callback::class);
}
$method = $constraint->callback;
if ($method instanceof \Closure) {
$method($object, $this->context, $constraint->payload);
} elseif (\is_array($method)) {
if (!\is_callable($method)) {
if (isset($method[0]) && \is_object($method[0])) {
$method[0] = $method[0]::class;
}
throw new ConstraintDefinitionException(json_encode($method).' targeted by Callback constraint is not a valid callable.');
}
$method($object, $this->context, $constraint->payload);
} elseif (null !== $object) {
if (!method_exists($object, $method)) {
throw new ConstraintDefinitionException(\sprintf('Method "%s" targeted by Callback constraint does not exist in class "%s".', $method, get_debug_type($object)));
}
$reflMethod = new \ReflectionMethod($object, $method);
if ($reflMethod->isStatic()) {
$reflMethod->invoke(null, $object, $this->context, $constraint->payload);
} else {
$reflMethod->invoke($object, $this->context, $constraint->payload);
}
}
}
}

View File

@@ -0,0 +1,79 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* Metadata for the CardSchemeValidator.
*
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Tim Nagel <t.nagel@infinite.net.au>
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class CardScheme extends Constraint
{
public const AMEX = 'AMEX';
public const CHINA_UNIONPAY = 'CHINA_UNIONPAY';
public const DINERS = 'DINERS';
public const DISCOVER = 'DISCOVER';
public const INSTAPAYMENT = 'INSTAPAYMENT';
public const JCB = 'JCB';
public const LASER = 'LASER';
public const MAESTRO = 'MAESTRO';
public const MASTERCARD = 'MASTERCARD';
public const MIR = 'MIR';
public const UATP = 'UATP';
public const VISA = 'VISA';
public const NOT_NUMERIC_ERROR = 'a2ad9231-e827-485f-8a1e-ef4d9a6d5c2e';
public const INVALID_FORMAT_ERROR = 'a8faedbf-1c2f-4695-8d22-55783be8efed';
protected const ERROR_NAMES = [
self::NOT_NUMERIC_ERROR => 'NOT_NUMERIC_ERROR',
self::INVALID_FORMAT_ERROR => 'INVALID_FORMAT_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $message = 'Unsupported card type or invalid card number.';
public $schemes;
public function __construct(array|string|null $schemes, ?string $message = null, ?array $groups = null, mixed $payload = null, array $options = [])
{
if (\is_array($schemes) && \is_string(key($schemes))) {
$options = array_merge($schemes, $options);
} elseif (null !== $schemes) {
$options['value'] = $schemes;
}
parent::__construct($options, $groups, $payload);
$this->message = $message ?? $this->message;
}
public function getDefaultOption(): ?string
{
return 'schemes';
}
public function getRequiredOptions(): array
{
return ['schemes'];
}
}

View File

@@ -0,0 +1,134 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
/**
* Validates that a card number belongs to a specified scheme.
*
* @author Tim Nagel <t.nagel@infinite.net.au>
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @see https://en.wikipedia.org/wiki/Payment_card_number
* @see https://www.regular-expressions.info/creditcard.html
*/
class CardSchemeValidator extends ConstraintValidator
{
protected $schemes = [
// American Express card numbers start with 34 or 37 and have 15 digits.
CardScheme::AMEX => [
'/^3[47][0-9]{13}$/D',
],
// China UnionPay cards start with 62 and have between 16 and 19 digits.
// Please note that these cards do not follow Luhn Algorithm as a checksum.
CardScheme::CHINA_UNIONPAY => [
'/^62[0-9]{14,17}$/D',
],
// Diners Club card numbers begin with 300 through 305, 36 or 38. All have 14 digits.
// There are Diners Club cards that begin with 5 and have 16 digits.
// These are a joint venture between Diners Club and MasterCard, and should be processed like a MasterCard.
CardScheme::DINERS => [
'/^3(?:0[0-5]|[68][0-9])[0-9]{11}$/D',
],
// Discover card numbers begin with 6011, 622126 through 622925, 644 through 649 or 65.
// All have 16 digits.
CardScheme::DISCOVER => [
'/^6011[0-9]{12}$/D',
'/^64[4-9][0-9]{13}$/D',
'/^65[0-9]{14}$/D',
'/^622(12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|91[0-9]|92[0-5])[0-9]{10}$/D',
],
// InstaPayment cards begin with 637 through 639 and have 16 digits.
CardScheme::INSTAPAYMENT => [
'/^63[7-9][0-9]{13}$/D',
],
// JCB cards beginning with 2131 or 1800 have 15 digits.
// JCB cards beginning with 35 have 16 digits.
CardScheme::JCB => [
'/^(?:2131|1800|35[0-9]{3})[0-9]{11}$/D',
],
// Laser cards begin with either 6304, 6706, 6709 or 6771 and have between 16 and 19 digits.
CardScheme::LASER => [
'/^(6304|670[69]|6771)[0-9]{12,15}$/D',
],
// Maestro international cards begin with 675900..675999 and have between 12 and 19 digits.
// Maestro UK cards begin with either 500000..509999 or 560000..699999 and have between 12 and 19 digits.
CardScheme::MAESTRO => [
'/^(6759[0-9]{2})[0-9]{6,13}$/D',
'/^(50[0-9]{4})[0-9]{6,13}$/D',
'/^5[6-9][0-9]{10,17}$/D',
'/^6[0-9]{11,18}$/D',
],
// All MasterCard numbers start with the numbers 51 through 55. All have 16 digits.
// October 2016 MasterCard numbers can also start with 222100 through 272099.
CardScheme::MASTERCARD => [
'/^5[1-5][0-9]{14}$/D',
'/^2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12})$/D',
],
// Payment system MIR numbers start with 220, then 1 digit from 0 to 4, then between 12 and 15 digits
CardScheme::MIR => [
'/^220[0-4][0-9]{12,15}$/D',
],
// All UATP card numbers start with a 1 and have a length of 15 digits.
CardScheme::UATP => [
'/^1[0-9]{14}$/D',
],
// All Visa card numbers start with a 4 and have a length of 13, 16, or 19 digits.
CardScheme::VISA => [
'/^4([0-9]{12}|[0-9]{15}|[0-9]{18})$/D',
],
];
/**
* Validates a creditcard belongs to a specified scheme.
*
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof CardScheme) {
throw new UnexpectedTypeException($constraint, CardScheme::class);
}
if (null === $value || '' === $value) {
return;
}
if (!is_numeric($value)) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(CardScheme::NOT_NUMERIC_ERROR)
->addViolation();
return;
}
$schemes = array_flip((array) $constraint->schemes);
$schemeRegexes = array_intersect_key($this->schemes, $schemes);
foreach ($schemeRegexes as $regexes) {
foreach ($regexes as $regex) {
if (preg_match($regex, $value)) {
return;
}
}
}
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(CardScheme::INVALID_FORMAT_ERROR)
->addViolation();
}
}

View File

@@ -0,0 +1,48 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
/**
* @Annotation
* @Target({"CLASS"})
*
* @author Jules Pietri <jules@heahprod.com>
*/
#[\Attribute(\Attribute::TARGET_CLASS)]
class Cascade extends Constraint
{
public array $exclude = [];
public function __construct(array|string|null $exclude = null, ?array $options = null)
{
if (\is_array($exclude) && !array_is_list($exclude)) {
$options = array_merge($exclude, $options ?? []);
$options['exclude'] = array_flip((array) ($options['exclude'] ?? []));
} else {
$this->exclude = array_flip((array) $exclude);
}
if (\is_array($options) && \array_key_exists('groups', $options)) {
throw new ConstraintDefinitionException(\sprintf('The option "groups" is not supported by the constraint "%s".', __CLASS__));
}
parent::__construct($options);
}
public function getTargets(): string|array
{
return self::CLASS_CONSTRAINT;
}
}

View File

@@ -0,0 +1,95 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Choice extends Constraint
{
public const NO_SUCH_CHOICE_ERROR = '8e179f1b-97aa-4560-a02f-2a8b42e49df7';
public const TOO_FEW_ERROR = '11edd7eb-5872-4b6e-9f12-89923999fd0e';
public const TOO_MANY_ERROR = '9bd98e49-211c-433f-8630-fd1c2d0f08c3';
protected const ERROR_NAMES = [
self::NO_SUCH_CHOICE_ERROR => 'NO_SUCH_CHOICE_ERROR',
self::TOO_FEW_ERROR => 'TOO_FEW_ERROR',
self::TOO_MANY_ERROR => 'TOO_MANY_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $choices;
/** @var callable|string|null */
public $callback;
public $multiple = false;
public $strict = true;
public $min;
public $max;
public $message = 'The value you selected is not a valid choice.';
public $multipleMessage = 'One or more of the given values is invalid.';
public $minMessage = 'You must select at least {{ limit }} choice.|You must select at least {{ limit }} choices.';
public $maxMessage = 'You must select at most {{ limit }} choice.|You must select at most {{ limit }} choices.';
public bool $match = true;
public function getDefaultOption(): ?string
{
return 'choices';
}
public function __construct(
string|array $options = [],
?array $choices = null,
callable|string|null $callback = null,
?bool $multiple = null,
?bool $strict = null,
?int $min = null,
?int $max = null,
?string $message = null,
?string $multipleMessage = null,
?string $minMessage = null,
?string $maxMessage = null,
?array $groups = null,
mixed $payload = null,
?bool $match = null,
) {
if (\is_array($options) && $options && array_is_list($options)) {
$choices ??= $options;
$options = [];
}
if (null !== $choices) {
$options['value'] = $choices;
}
parent::__construct($options, $groups, $payload);
$this->callback = $callback ?? $this->callback;
$this->multiple = $multiple ?? $this->multiple;
$this->strict = $strict ?? $this->strict;
$this->min = $min ?? $this->min;
$this->max = $max ?? $this->max;
$this->message = $message ?? $this->message;
$this->multipleMessage = $multipleMessage ?? $this->multipleMessage;
$this->minMessage = $minMessage ?? $this->minMessage;
$this->maxMessage = $maxMessage ?? $this->maxMessage;
$this->match = $match ?? $this->match;
}
}

View File

@@ -0,0 +1,113 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Exception\RuntimeException;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
/**
* ChoiceValidator validates that the value is one of the expected values.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Florian Eckerstorfer <florian@eckerstorfer.org>
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class ChoiceValidator extends ConstraintValidator
{
/**
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof Choice) {
throw new UnexpectedTypeException($constraint, Choice::class);
}
if (!\is_array($constraint->choices) && !$constraint->callback) {
throw new ConstraintDefinitionException('Either "choices" or "callback" must be specified on constraint Choice.');
}
if (null === $value) {
return;
}
if ($constraint->multiple && !\is_array($value)) {
throw new UnexpectedValueException($value, 'array');
}
if ($constraint->callback) {
if (!\is_callable($choices = [$this->context->getObject(), $constraint->callback])
&& !\is_callable($choices = [$this->context->getClassName(), $constraint->callback])
&& !\is_callable($choices = $constraint->callback)
) {
throw new ConstraintDefinitionException('The Choice constraint expects a valid callback.');
}
$choices = $choices();
if (!\is_array($choices)) {
throw new ConstraintDefinitionException(\sprintf('The Choice constraint callback "%s" is expected to return an array, but returned "%s".', trim($this->formatValue($constraint->callback), '"'), get_debug_type($choices)));
}
} else {
$choices = $constraint->choices;
}
if (true !== $constraint->strict) {
throw new RuntimeException('The "strict" option of the Choice constraint should not be used.');
}
if ($constraint->multiple) {
foreach ($value as $_value) {
if ($constraint->match xor \in_array($_value, $choices, true)) {
$this->context->buildViolation($constraint->multipleMessage)
->setParameter('{{ value }}', $this->formatValue($_value))
->setParameter('{{ choices }}', $this->formatValues($choices))
->setCode(Choice::NO_SUCH_CHOICE_ERROR)
->setInvalidValue($_value)
->addViolation();
return;
}
}
$count = \count($value);
if (null !== $constraint->min && $count < $constraint->min) {
$this->context->buildViolation($constraint->minMessage)
->setParameter('{{ limit }}', $constraint->min)
->setPlural((int) $constraint->min)
->setCode(Choice::TOO_FEW_ERROR)
->addViolation();
return;
}
if (null !== $constraint->max && $count > $constraint->max) {
$this->context->buildViolation($constraint->maxMessage)
->setParameter('{{ limit }}', $constraint->max)
->setPlural((int) $constraint->max)
->setCode(Choice::TOO_MANY_ERROR)
->addViolation();
return;
}
} elseif ($constraint->match xor \in_array($value, $choices, true)) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setParameter('{{ choices }}', $this->formatValues($choices))
->setCode(Choice::NO_SUCH_CHOICE_ERROR)
->addViolation();
}
}
}

View File

@@ -0,0 +1,85 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
/**
* Validates that a value is a valid CIDR notation.
*
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Sorin Pop <popsorin15@gmail.com>
* @author Calin Bolea <calin.bolea@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Cidr extends Constraint
{
public const INVALID_CIDR_ERROR = '5649e53a-5afb-47c5-a360-ffbab3be8567';
public const OUT_OF_RANGE_ERROR = 'b9f14a51-acbd-401a-a078-8c6b204ab32f';
protected const ERROR_NAMES = [
self::INVALID_CIDR_ERROR => 'INVALID_CIDR_ERROR',
self::OUT_OF_RANGE_ERROR => 'OUT_OF_RANGE_VIOLATION',
];
private const NET_MAXES = [
Ip::ALL => 128,
Ip::V4 => 32,
Ip::V6 => 128,
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $version = Ip::ALL;
public $message = 'This value is not a valid CIDR notation.';
public $netmaskRangeViolationMessage = 'The value of the netmask should be between {{ min }} and {{ max }}.';
public $netmaskMin = 0;
public $netmaskMax;
public function __construct(
?array $options = null,
?string $version = null,
?int $netmaskMin = null,
?int $netmaskMax = null,
?string $message = null,
?array $groups = null,
$payload = null,
) {
$this->version = $version ?? $options['version'] ?? $this->version;
if (!\array_key_exists($this->version, self::NET_MAXES)) {
throw new ConstraintDefinitionException(\sprintf('The option "version" must be one of "%s".', implode('", "', array_keys(self::NET_MAXES))));
}
$this->netmaskMin = $netmaskMin ?? $options['netmaskMin'] ?? $this->netmaskMin;
$this->netmaskMax = $netmaskMax ?? $options['netmaskMax'] ?? self::NET_MAXES[$this->version];
$this->message = $message ?? $this->message;
unset($options['netmaskMin'], $options['netmaskMax'], $options['version']);
if ($this->netmaskMin < 0 || $this->netmaskMax > self::NET_MAXES[$this->version] || $this->netmaskMin > $this->netmaskMax) {
throw new ConstraintDefinitionException(\sprintf('The netmask range must be between 0 and %d.', self::NET_MAXES[$this->version]));
}
parent::__construct($options, $groups, $payload);
}
}

View File

@@ -0,0 +1,77 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
class CidrValidator extends ConstraintValidator
{
public function validate($value, Constraint $constraint): void
{
if (!$constraint instanceof Cidr) {
throw new UnexpectedTypeException($constraint, Cidr::class);
}
if (null === $value || '' === $value) {
return;
}
if (!\is_string($value)) {
throw new UnexpectedValueException($value, 'string');
}
$cidrParts = explode('/', $value, 2);
if (!isset($cidrParts[1])
|| !ctype_digit($cidrParts[1])
|| '' === $cidrParts[0]
) {
$this->context
->buildViolation($constraint->message)
->setCode(Cidr::INVALID_CIDR_ERROR)
->addViolation();
return;
}
$ipAddress = $cidrParts[0];
$netmask = (int) $cidrParts[1];
$validV4 = Ip::V6 !== $constraint->version
&& filter_var($ipAddress, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4)
&& $netmask <= 32;
$validV6 = Ip::V4 !== $constraint->version
&& filter_var($ipAddress, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6);
if (!$validV4 && !$validV6) {
$this->context
->buildViolation($constraint->message)
->setCode(Cidr::INVALID_CIDR_ERROR)
->addViolation();
return;
}
if ($netmask < $constraint->netmaskMin || $netmask > $constraint->netmaskMax) {
$this->context
->buildViolation($constraint->netmaskRangeViolationMessage)
->setParameter('{{ min }}', $constraint->netmaskMin)
->setParameter('{{ max }}', $constraint->netmaskMax)
->setCode(Cidr::OUT_OF_RANGE_ERROR)
->addViolation();
}
}
}

View File

@@ -0,0 +1,119 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Collection extends Composite
{
public const MISSING_FIELD_ERROR = '2fa2158c-2a7f-484b-98aa-975522539ff8';
public const NO_SUCH_FIELD_ERROR = '7703c766-b5d5-4cef-ace7-ae0dd82304e9';
protected const ERROR_NAMES = [
self::MISSING_FIELD_ERROR => 'MISSING_FIELD_ERROR',
self::NO_SUCH_FIELD_ERROR => 'NO_SUCH_FIELD_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $fields = [];
public $allowExtraFields = false;
public $allowMissingFields = false;
public $extraFieldsMessage = 'This field was not expected.';
public $missingFieldsMessage = 'This field is missing.';
public function __construct(mixed $fields = null, ?array $groups = null, mixed $payload = null, ?bool $allowExtraFields = null, ?bool $allowMissingFields = null, ?string $extraFieldsMessage = null, ?string $missingFieldsMessage = null)
{
if (self::isFieldsOption($fields)) {
$fields = ['fields' => $fields];
}
parent::__construct($fields, $groups, $payload);
$this->allowExtraFields = $allowExtraFields ?? $this->allowExtraFields;
$this->allowMissingFields = $allowMissingFields ?? $this->allowMissingFields;
$this->extraFieldsMessage = $extraFieldsMessage ?? $this->extraFieldsMessage;
$this->missingFieldsMessage = $missingFieldsMessage ?? $this->missingFieldsMessage;
}
/**
* @return void
*/
protected function initializeNestedConstraints()
{
parent::initializeNestedConstraints();
if (!\is_array($this->fields)) {
throw new ConstraintDefinitionException(\sprintf('The option "fields" is expected to be an array in constraint "%s".', __CLASS__));
}
foreach ($this->fields as $fieldName => $field) {
// the XmlFileLoader and YamlFileLoader pass the field Optional
// and Required constraint as an array with exactly one element
if (\is_array($field) && 1 == \count($field)) {
$this->fields[$fieldName] = $field = $field[0];
}
if (!$field instanceof Optional && !$field instanceof Required) {
$this->fields[$fieldName] = new Required($field);
}
}
}
public function getRequiredOptions(): array
{
return ['fields'];
}
protected function getCompositeOption(): string
{
return 'fields';
}
private static function isFieldsOption($options): bool
{
if (!\is_array($options)) {
return false;
}
foreach ($options as $optionOrField) {
if ($optionOrField instanceof Constraint) {
return true;
}
if (null === $optionOrField) {
continue;
}
if (!\is_array($optionOrField)) {
return false;
}
if ($optionOrField && !($optionOrField[0] ?? null) instanceof Constraint) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,86 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class CollectionValidator extends ConstraintValidator
{
/**
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof Collection) {
throw new UnexpectedTypeException($constraint, Collection::class);
}
if (null === $value) {
return;
}
if (!\is_array($value) && !($value instanceof \Traversable && $value instanceof \ArrayAccess)) {
throw new UnexpectedValueException($value, 'array|(Traversable&ArrayAccess)');
}
// We need to keep the initialized context when CollectionValidator
// calls itself recursively (Collection constraints can be nested).
// Since the context of the validator is overwritten when initialize()
// is called for the nested constraint, the outer validator is
// acting on the wrong context when the nested validation terminates.
//
// A better solution - which should be approached in Symfony 3.0 - is to
// remove the initialize() method and pass the context as last argument
// to validate() instead.
$context = $this->context;
foreach ($constraint->fields as $field => $fieldConstraint) {
$existsInArray = \is_array($value) && \array_key_exists($field, $value);
$existsInArrayAccess = $value instanceof \ArrayAccess && $value->offsetExists($field);
if ($existsInArray || $existsInArrayAccess) {
if (\count($fieldConstraint->constraints) > 0) {
$context->getValidator()
->inContext($context)
->atPath('['.$field.']')
->validate($value[$field], $fieldConstraint->constraints);
}
} elseif (!$fieldConstraint instanceof Optional && !$constraint->allowMissingFields) {
$context->buildViolation($constraint->missingFieldsMessage)
->atPath('['.$field.']')
->setParameter('{{ field }}', $this->formatValue($field))
->setInvalidValue(null)
->setCode(Collection::MISSING_FIELD_ERROR)
->addViolation();
}
}
if (!$constraint->allowExtraFields) {
foreach ($value as $field => $fieldValue) {
if (!isset($constraint->fields[$field])) {
$context->buildViolation($constraint->extraFieldsMessage)
->atPath('['.$field.']')
->setParameter('{{ field }}', $this->formatValue($field))
->setInvalidValue($fieldValue)
->setCode(Collection::NO_SUCH_FIELD_ERROR)
->addViolation();
}
}
}
}
}

View File

@@ -0,0 +1,157 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
/**
* A constraint that is composed of other constraints.
*
* You should never use the nested constraint instances anywhere else, because
* their groups are adapted when passed to the constructor of this class.
*
* If you want to create your own composite constraint, extend this class and
* let {@link getCompositeOption()} return the name of the property which
* contains the nested constraints.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
abstract class Composite extends Constraint
{
/**
* The groups of the composite and its nested constraints are made
* consistent using the following strategy:
*
* - If groups are passed explicitly to the composite constraint, but
* not to the nested constraints, the options of the composite
* constraint are copied to the nested constraints;
*
* - If groups are passed explicitly to the nested constraints, but not
* to the composite constraint, the groups of all nested constraints
* are merged and used as groups for the composite constraint;
*
* - If groups are passed explicitly to both the composite and its nested
* constraints, the groups of the nested constraints must be a subset
* of the groups of the composite constraint. If not, a
* {@link ConstraintDefinitionException} is thrown.
*
* All this is done in the constructor, because constraints can then be
* cached. When constraints are loaded from the cache, no more group
* checks need to be done.
*/
public function __construct(mixed $options = null, ?array $groups = null, mixed $payload = null)
{
parent::__construct($options, $groups, $payload);
$this->initializeNestedConstraints();
/** @var Constraint[] $nestedConstraints */
$compositeOption = $this->getCompositeOption();
$nestedConstraints = $this->$compositeOption;
if (!\is_array($nestedConstraints)) {
$nestedConstraints = [$nestedConstraints];
}
foreach ($nestedConstraints as $constraint) {
if (!$constraint instanceof Constraint) {
if (\is_object($constraint)) {
$constraint = $constraint::class;
}
throw new ConstraintDefinitionException(\sprintf('The value "%s" is not an instance of Constraint in constraint "%s".', $constraint, static::class));
}
if ($constraint instanceof Valid) {
throw new ConstraintDefinitionException(\sprintf('The constraint Valid cannot be nested inside constraint "%s". You can only declare the Valid constraint directly on a field or method.', static::class));
}
}
if (!isset(((array) $this)['groups'])) {
$mergedGroups = [];
foreach ($nestedConstraints as $constraint) {
foreach ($constraint->groups as $group) {
$mergedGroups[$group] = true;
}
}
// prevent empty composite constraint to have empty groups
$this->groups = array_keys($mergedGroups) ?: [self::DEFAULT_GROUP];
$this->$compositeOption = $nestedConstraints;
return;
}
foreach ($nestedConstraints as $constraint) {
if (isset(((array) $constraint)['groups'])) {
$excessGroups = array_diff($constraint->groups, $this->groups);
if (\count($excessGroups) > 0) {
throw new ConstraintDefinitionException(\sprintf('The group(s) "%s" passed to the constraint "%s" should also be passed to its containing constraint "%s".', implode('", "', $excessGroups), get_debug_type($constraint), static::class));
}
} else {
$constraint->groups = $this->groups;
}
}
$this->$compositeOption = $nestedConstraints;
}
/**
* Implicit group names are forwarded to nested constraints.
*
* @return void
*/
public function addImplicitGroupName(string $group)
{
parent::addImplicitGroupName($group);
/** @var Constraint[] $nestedConstraints */
$nestedConstraints = $this->{$this->getCompositeOption()};
foreach ($nestedConstraints as $constraint) {
$constraint->addImplicitGroupName($group);
}
}
/**
* Returns the name of the property that contains the nested constraints.
*/
abstract protected function getCompositeOption(): string;
/**
* @internal Used by metadata
*
* @return Constraint[]
*/
public function getNestedConstraints(): array
{
/** @var Constraint[] $nestedConstraints */
return $this->{$this->getCompositeOption()};
}
/**
* Initializes the nested constraints.
*
* This method can be overwritten in subclasses to clean up the nested
* constraints passed to the constructor.
*
* @see Collection::initializeNestedConstraints()
*
* @return void
*/
protected function initializeNestedConstraints()
{
}
}

View File

@@ -0,0 +1,52 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
/**
* Extend this class to create a reusable set of constraints.
*
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
*/
abstract class Compound extends Composite
{
/** @var Constraint[] */
public $constraints = [];
public function __construct(mixed $options = null)
{
if (isset($options[$this->getCompositeOption()])) {
throw new ConstraintDefinitionException(\sprintf('You can\'t redefine the "%s" option. Use the "%s::getConstraints()" method instead.', $this->getCompositeOption(), __CLASS__));
}
$this->constraints = $this->getConstraints($this->normalizeOptions($options));
parent::__construct($options);
}
final protected function getCompositeOption(): string
{
return 'constraints';
}
final public function validatedBy(): string
{
return CompoundValidator::class;
}
/**
* @return Constraint[]
*/
abstract protected function getConstraints(array $options): array;
}

View File

@@ -0,0 +1,38 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
/**
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
*/
class CompoundValidator extends ConstraintValidator
{
/**
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof Compound) {
throw new UnexpectedTypeException($constraint, Compound::class);
}
$context = $this->context;
$validator = $context->getValidator()->inContext($context);
$validator->validate($value, $constraint->constraints);
}
}

View File

@@ -0,0 +1,92 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\MissingOptionsException;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Count extends Constraint
{
public const TOO_FEW_ERROR = 'bef8e338-6ae5-4caf-b8e2-50e7b0579e69';
public const TOO_MANY_ERROR = '756b1212-697c-468d-a9ad-50dd783bb169';
public const NOT_EQUAL_COUNT_ERROR = '9fe5d43f-3784-4ece-a0e1-473fc02dadbc';
public const NOT_DIVISIBLE_BY_ERROR = DivisibleBy::NOT_DIVISIBLE_BY;
protected const ERROR_NAMES = [
self::TOO_FEW_ERROR => 'TOO_FEW_ERROR',
self::TOO_MANY_ERROR => 'TOO_MANY_ERROR',
self::NOT_EQUAL_COUNT_ERROR => 'NOT_EQUAL_COUNT_ERROR',
self::NOT_DIVISIBLE_BY_ERROR => 'NOT_DIVISIBLE_BY_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $minMessage = 'This collection should contain {{ limit }} element or more.|This collection should contain {{ limit }} elements or more.';
public $maxMessage = 'This collection should contain {{ limit }} element or less.|This collection should contain {{ limit }} elements or less.';
public $exactMessage = 'This collection should contain exactly {{ limit }} element.|This collection should contain exactly {{ limit }} elements.';
public $divisibleByMessage = 'The number of elements in this collection should be a multiple of {{ compared_value }}.';
public $min;
public $max;
public $divisibleBy;
public function __construct(
int|array|null $exactly = null,
?int $min = null,
?int $max = null,
?int $divisibleBy = null,
?string $exactMessage = null,
?string $minMessage = null,
?string $maxMessage = null,
?string $divisibleByMessage = null,
?array $groups = null,
mixed $payload = null,
array $options = [],
) {
if (\is_array($exactly)) {
$options = array_merge($exactly, $options);
$exactly = $options['value'] ?? null;
}
$min ??= $options['min'] ?? null;
$max ??= $options['max'] ?? null;
unset($options['value'], $options['min'], $options['max']);
if (null !== $exactly && null === $min && null === $max) {
$min = $max = $exactly;
}
parent::__construct($options, $groups, $payload);
$this->min = $min;
$this->max = $max;
$this->divisibleBy = $divisibleBy ?? $this->divisibleBy;
$this->exactMessage = $exactMessage ?? $this->exactMessage;
$this->minMessage = $minMessage ?? $this->minMessage;
$this->maxMessage = $maxMessage ?? $this->maxMessage;
$this->divisibleByMessage = $divisibleByMessage ?? $this->divisibleByMessage;
if (null === $this->min && null === $this->max && null === $this->divisibleBy) {
throw new MissingOptionsException(\sprintf('Either option "min", "max" or "divisibleBy" must be given for constraint "%s".', __CLASS__), ['min', 'max', 'divisibleBy']);
}
}
}

View File

@@ -0,0 +1,83 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class CountValidator extends ConstraintValidator
{
/**
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof Count) {
throw new UnexpectedTypeException($constraint, Count::class);
}
if (null === $value) {
return;
}
if (!\is_array($value) && !$value instanceof \Countable) {
throw new UnexpectedValueException($value, 'array|\Countable');
}
$count = \count($value);
if (null !== $constraint->max && $count > $constraint->max) {
$exactlyOptionEnabled = $constraint->min == $constraint->max;
$this->context->buildViolation($exactlyOptionEnabled ? $constraint->exactMessage : $constraint->maxMessage)
->setParameter('{{ count }}', $count)
->setParameter('{{ limit }}', $constraint->max)
->setInvalidValue($value)
->setPlural((int) $constraint->max)
->setCode($exactlyOptionEnabled ? Count::NOT_EQUAL_COUNT_ERROR : Count::TOO_MANY_ERROR)
->addViolation();
return;
}
if (null !== $constraint->min && $count < $constraint->min) {
$exactlyOptionEnabled = $constraint->min == $constraint->max;
$this->context->buildViolation($exactlyOptionEnabled ? $constraint->exactMessage : $constraint->minMessage)
->setParameter('{{ count }}', $count)
->setParameter('{{ limit }}', $constraint->min)
->setInvalidValue($value)
->setPlural((int) $constraint->min)
->setCode($exactlyOptionEnabled ? Count::NOT_EQUAL_COUNT_ERROR : Count::TOO_FEW_ERROR)
->addViolation();
return;
}
if (null !== $constraint->divisibleBy) {
$this->context
->getValidator()
->inContext($this->context)
->validate($count, [
new DivisibleBy([
'value' => $constraint->divisibleBy,
'message' => $constraint->divisibleByMessage,
]),
], $this->context->getGroup());
}
}
}

View File

@@ -0,0 +1,57 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Intl\Countries;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\LogicException;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Country extends Constraint
{
public const NO_SUCH_COUNTRY_ERROR = '8f900c12-61bd-455d-9398-996cd040f7f0';
protected const ERROR_NAMES = [
self::NO_SUCH_COUNTRY_ERROR => 'NO_SUCH_COUNTRY_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $message = 'This value is not a valid country.';
public $alpha3 = false;
public function __construct(
?array $options = null,
?string $message = null,
?bool $alpha3 = null,
?array $groups = null,
mixed $payload = null,
) {
if (!class_exists(Countries::class)) {
throw new LogicException('The Intl component is required to use the Country constraint. Try running "composer require symfony/intl".');
}
parent::__construct($options, $groups, $payload);
$this->message = $message ?? $this->message;
$this->alpha3 = $alpha3 ?? $this->alpha3;
}
}

View File

@@ -0,0 +1,53 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Intl\Countries;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
/**
* Validates whether a value is a valid country code.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class CountryValidator extends ConstraintValidator
{
/**
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof Country) {
throw new UnexpectedTypeException($constraint, Country::class);
}
if (null === $value || '' === $value) {
return;
}
if (!\is_scalar($value) && !$value instanceof \Stringable) {
throw new UnexpectedValueException($value, 'string');
}
$value = (string) $value;
if ($constraint->alpha3 ? !Countries::alpha3CodeExists($value) : !Countries::exists($value)) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(Country::NO_SUCH_COUNTRY_ERROR)
->addViolation();
}
}
}

View File

@@ -0,0 +1,111 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\InvalidArgumentException;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Mathieu Santostefano <msantostefano@protonmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class CssColor extends Constraint
{
public const HEX_LONG = 'hex_long';
public const HEX_LONG_WITH_ALPHA = 'hex_long_with_alpha';
public const HEX_SHORT = 'hex_short';
public const HEX_SHORT_WITH_ALPHA = 'hex_short_with_alpha';
public const BASIC_NAMED_COLORS = 'basic_named_colors';
public const EXTENDED_NAMED_COLORS = 'extended_named_colors';
public const SYSTEM_COLORS = 'system_colors';
public const KEYWORDS = 'keywords';
public const RGB = 'rgb';
public const RGBA = 'rgba';
public const HSL = 'hsl';
public const HSLA = 'hsla';
public const INVALID_FORMAT_ERROR = '454ab47b-aacf-4059-8f26-184b2dc9d48d';
protected const ERROR_NAMES = [
self::INVALID_FORMAT_ERROR => 'INVALID_FORMAT_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
/**
* @var string[]
*/
private static array $validationModes = [
self::HEX_LONG,
self::HEX_LONG_WITH_ALPHA,
self::HEX_SHORT,
self::HEX_SHORT_WITH_ALPHA,
self::BASIC_NAMED_COLORS,
self::EXTENDED_NAMED_COLORS,
self::SYSTEM_COLORS,
self::KEYWORDS,
self::RGB,
self::RGBA,
self::HSL,
self::HSLA,
];
public $message = 'This value is not a valid CSS color.';
public $formats;
/**
* @param array|string $formats The types of CSS colors allowed (e.g. hexadecimal only, RGB and HSL only, etc.).
*/
public function __construct($formats = [], ?string $message = null, ?array $groups = null, $payload = null, ?array $options = null)
{
$validationModesAsString = implode(', ', self::$validationModes);
if (!$formats) {
$options['value'] = self::$validationModes;
} elseif (\is_array($formats) && \is_string(key($formats))) {
$options = array_merge($formats, $options ?? []);
} elseif (\is_array($formats)) {
if ([] === array_intersect(self::$validationModes, $formats)) {
throw new InvalidArgumentException(\sprintf('The "formats" parameter value is not valid. It must contain one or more of the following values: "%s".', $validationModesAsString));
}
$options['value'] = $formats;
} elseif (\is_string($formats)) {
if (!\in_array($formats, self::$validationModes)) {
throw new InvalidArgumentException(\sprintf('The "formats" parameter value is not valid. It must contain one or more of the following values: "%s".', $validationModesAsString));
}
$options['value'] = [$formats];
} else {
throw new InvalidArgumentException('The "formats" parameter type is not valid. It should be a string or an array.');
}
parent::__construct($options, $groups, $payload);
$this->message = $message ?? $this->message;
}
public function getDefaultOption(): string
{
return 'formats';
}
public function getRequiredOptions(): array
{
return ['formats'];
}
}

View File

@@ -0,0 +1,83 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
/**
* @author Mathieu Santostefano <msantostefano@protonmail.com>
*/
class CssColorValidator extends ConstraintValidator
{
private const PATTERN_HEX_LONG = '/^#[0-9a-f]{6}$/iD';
private const PATTERN_HEX_LONG_WITH_ALPHA = '/^#[0-9a-f]{8}$/iD';
private const PATTERN_HEX_SHORT = '/^#[0-9a-f]{3}$/iD';
private const PATTERN_HEX_SHORT_WITH_ALPHA = '/^#[0-9a-f]{4}$/iD';
// List comes from https://www.w3.org/wiki/CSS/Properties/color/keywords#Basic_Colors
private const PATTERN_BASIC_NAMED_COLORS = '/^(black|silver|gray|white|maroon|red|purple|fuchsia|green|lime|olive|yellow|navy|blue|teal|aqua)$/iD';
// List comes from https://www.w3.org/wiki/CSS/Properties/color/keywords#Extended_colors
private const PATTERN_EXTENDED_NAMED_COLORS = '/^(aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgreen|darkgrey|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|firebrick|floralwhite|forestgreen|fuchsia|gainsboro|ghostwhite|gold|goldenrod|gray|green|greenyellow|grey|honeydew|hotpink|indianred|indigo|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgray|lightgreen|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightslategrey|lightsteelblue|lightyellow|lime|limegreen|linen|magenta|maroon|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|navy|oldlace|olive|olivedrab|orange|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|purple|red|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|slategrey|snow|springgreen|steelblue|tan|teal|thistle|tomato|turquoise|violet|wheat|white|whitesmoke|yellow|yellowgreen)$/iD';
// List comes from https://drafts.csswg.org/css-color/#css-system-colors
private const PATTERN_SYSTEM_COLORS = '/^(Canvas|CanvasText|LinkText|VisitedText|ActiveText|ButtonFace|ButtonText|ButtonBorder|Field|FieldText|Highlight|HighlightText|SelectedItem|SelectedItemText|Mark|MarkText|GrayText)$/iD';
private const PATTERN_KEYWORDS = '/^(transparent|currentColor)$/iD';
private const PATTERN_RGB = '/^rgb\(\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d)\s*\)$/iD';
private const PATTERN_RGBA = '/^rgba\(\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),\s*(0|0?\.\d+|1(\.0)?)\s*\)$/iD';
private const PATTERN_HSL = '/^hsl\(\s*(0|360|35\d|3[0-4]\d|[12]\d\d|0?\d?\d),\s*(0|100|\d{1,2})%,\s*(0|100|\d{1,2})%\s*\)$/iD';
private const PATTERN_HSLA = '/^hsla\(\s*(0|360|35\d|3[0-4]\d|[12]\d\d|0?\d?\d),\s*(0|100|\d{1,2})%,\s*(0|100|\d{1,2})%,\s*(0|0?\.\d+|1(\.0)?)\s*\)$/iD';
private const COLOR_PATTERNS = [
CssColor::HEX_LONG => self::PATTERN_HEX_LONG,
CssColor::HEX_LONG_WITH_ALPHA => self::PATTERN_HEX_LONG_WITH_ALPHA,
CssColor::HEX_SHORT => self::PATTERN_HEX_SHORT,
CssColor::HEX_SHORT_WITH_ALPHA => self::PATTERN_HEX_SHORT_WITH_ALPHA,
CssColor::BASIC_NAMED_COLORS => self::PATTERN_BASIC_NAMED_COLORS,
CssColor::EXTENDED_NAMED_COLORS => self::PATTERN_EXTENDED_NAMED_COLORS,
CssColor::SYSTEM_COLORS => self::PATTERN_SYSTEM_COLORS,
CssColor::KEYWORDS => self::PATTERN_KEYWORDS,
CssColor::RGB => self::PATTERN_RGB,
CssColor::RGBA => self::PATTERN_RGBA,
CssColor::HSL => self::PATTERN_HSL,
CssColor::HSLA => self::PATTERN_HSLA,
];
public function validate($value, Constraint $constraint): void
{
if (!$constraint instanceof CssColor) {
throw new UnexpectedTypeException($constraint, CssColor::class);
}
if (null === $value || '' === $value) {
return;
}
if (!\is_string($value)) {
throw new UnexpectedValueException($value, 'string');
}
$formats = array_flip((array) $constraint->formats);
$formatRegexes = array_intersect_key(self::COLOR_PATTERNS, $formats);
foreach ($formatRegexes as $regex) {
if (preg_match($regex, (string) $value)) {
return;
}
}
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(CssColor::INVALID_FORMAT_ERROR)
->addViolation();
}
}

View File

@@ -0,0 +1,51 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Intl\Currencies;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\LogicException;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Miha Vrhovnik <miha.vrhovnik@pagein.si>
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Currency extends Constraint
{
public const NO_SUCH_CURRENCY_ERROR = '69945ac1-2db4-405f-bec7-d2772f73df52';
protected const ERROR_NAMES = [
self::NO_SUCH_CURRENCY_ERROR => 'NO_SUCH_CURRENCY_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $message = 'This value is not a valid currency.';
public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null)
{
if (!class_exists(Currencies::class)) {
throw new LogicException('The Intl component is required to use the Currency constraint. Try running "composer require symfony/intl".');
}
parent::__construct($options, $groups, $payload);
$this->message = $message ?? $this->message;
}
}

View File

@@ -0,0 +1,54 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Intl\Currencies;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
/**
* Validates whether a value is a valid currency.
*
* @author Miha Vrhovnik <miha.vrhovnik@pagein.si>
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class CurrencyValidator extends ConstraintValidator
{
/**
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof Currency) {
throw new UnexpectedTypeException($constraint, Currency::class);
}
if (null === $value || '' === $value) {
return;
}
if (!\is_scalar($value) && !$value instanceof \Stringable) {
throw new UnexpectedValueException($value, 'string');
}
$value = (string) $value;
if (!Currencies::exists($value)) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(Currency::NO_SUCH_CURRENCY_ERROR)
->addViolation();
}
}
}

View File

@@ -0,0 +1,46 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Date extends Constraint
{
public const INVALID_FORMAT_ERROR = '69819696-02ac-4a99-9ff0-14e127c4d1bc';
public const INVALID_DATE_ERROR = '3c184ce5-b31d-4de7-8b76-326da7b2be93';
protected const ERROR_NAMES = [
self::INVALID_FORMAT_ERROR => 'INVALID_FORMAT_ERROR',
self::INVALID_DATE_ERROR => 'INVALID_DATE_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $message = 'This value is not a valid date.';
public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null)
{
parent::__construct($options, $groups, $payload);
$this->message = $message ?? $this->message;
}
}

View File

@@ -0,0 +1,60 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class DateTime extends Constraint
{
public const INVALID_FORMAT_ERROR = '1a9da513-2640-4f84-9b6a-4d99dcddc628';
public const INVALID_DATE_ERROR = 'd52afa47-620d-4d99-9f08-f4d85b36e33c';
public const INVALID_TIME_ERROR = '5e797c9d-74f7-4098-baa3-94390c447b27';
protected const ERROR_NAMES = [
self::INVALID_FORMAT_ERROR => 'INVALID_FORMAT_ERROR',
self::INVALID_DATE_ERROR => 'INVALID_DATE_ERROR',
self::INVALID_TIME_ERROR => 'INVALID_TIME_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $format = 'Y-m-d H:i:s';
public $message = 'This value is not a valid datetime.';
public function __construct(string|array|null $format = null, ?string $message = null, ?array $groups = null, mixed $payload = null, array $options = [])
{
if (\is_array($format)) {
$options = array_merge($format, $options);
} elseif (null !== $format) {
$options['value'] = $format;
}
parent::__construct($options, $groups, $payload);
$this->message = $message ?? $this->message;
}
public function getDefaultOption(): ?string
{
return 'format';
}
}

View File

@@ -0,0 +1,79 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
* @author Diego Saint Esteben <diego@saintesteben.me>
*/
class DateTimeValidator extends DateValidator
{
/**
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof DateTime) {
throw new UnexpectedTypeException($constraint, DateTime::class);
}
if (null === $value || '' === $value) {
return;
}
if (!\is_scalar($value) && !$value instanceof \Stringable) {
throw new UnexpectedValueException($value, 'string');
}
$value = (string) $value;
\DateTimeImmutable::createFromFormat($constraint->format, $value);
$errors = \DateTimeImmutable::getLastErrors() ?: ['error_count' => 0, 'warnings' => []];
if (0 < $errors['error_count']) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(DateTime::INVALID_FORMAT_ERROR)
->addViolation();
return;
}
if (str_ends_with($constraint->format, '+')) {
$errors['warnings'] = array_filter($errors['warnings'], fn ($warning) => 'Trailing data' !== $warning);
}
foreach ($errors['warnings'] as $warning) {
if ('The parsed date was invalid' === $warning) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(DateTime::INVALID_DATE_ERROR)
->addViolation();
} elseif ('The parsed time was invalid' === $warning) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(DateTime::INVALID_TIME_ERROR)
->addViolation();
} else {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(DateTime::INVALID_FORMAT_ERROR)
->addViolation();
}
}
}
}

View File

@@ -0,0 +1,75 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class DateValidator extends ConstraintValidator
{
public const PATTERN = '/^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})$/D';
/**
* Checks whether a date is valid.
*
* @internal
*/
public static function checkDate(int $year, int $month, int $day): bool
{
return checkdate($month, $day, $year);
}
/**
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof Date) {
throw new UnexpectedTypeException($constraint, Date::class);
}
if (null === $value || '' === $value) {
return;
}
if (!\is_scalar($value) && !$value instanceof \Stringable) {
throw new UnexpectedValueException($value, 'string');
}
$value = (string) $value;
if (!preg_match(static::PATTERN, $value, $matches)) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(Date::INVALID_FORMAT_ERROR)
->addViolation();
return;
}
if (!self::checkDate(
$matches['year'] ?? $matches[1],
$matches['month'] ?? $matches[2],
$matches['day'] ?? $matches[3]
)) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(Date::INVALID_DATE_ERROR)
->addViolation();
}
}
}

View File

@@ -0,0 +1,43 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
/**
* Disables auto mapping.
*
* Using the annotations on a property has higher precedence than using it on a class,
* which has higher precedence than any configuration that might be defined outside the class.
*
* @Annotation
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::TARGET_CLASS)]
class DisableAutoMapping extends Constraint
{
public function __construct(?array $options = null)
{
if (\is_array($options) && \array_key_exists('groups', $options)) {
throw new ConstraintDefinitionException(\sprintf('The option "groups" is not supported by the constraint "%s".', __CLASS__));
}
parent::__construct($options);
}
public function getTargets(): string|array
{
return [self::PROPERTY_CONSTRAINT, self::CLASS_CONSTRAINT];
}
}

View File

@@ -0,0 +1,35 @@
<?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\Component\Validator\Constraints;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Colin O'Dell <colinodell@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class DivisibleBy extends AbstractComparison
{
public const NOT_DIVISIBLE_BY = '6d99d6c3-1464-4ccf-bdc7-14d083cf455c';
protected const ERROR_NAMES = [
self::NOT_DIVISIBLE_BY => 'NOT_DIVISIBLE_BY',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $message = 'This value should be a multiple of {{ compared_value }}.';
}

View File

@@ -0,0 +1,56 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
/**
* Validates that values are a multiple of the given number.
*
* @author Colin O'Dell <colinodell@gmail.com>
*/
class DivisibleByValidator extends AbstractComparisonValidator
{
protected function compareValues(mixed $value1, mixed $value2): bool
{
if (!is_numeric($value1)) {
throw new UnexpectedValueException($value1, 'numeric');
}
if (!is_numeric($value2)) {
throw new UnexpectedValueException($value2, 'numeric');
}
if (!$value2 = abs($value2)) {
return false;
}
if (\is_int($value1 = abs($value1)) && \is_int($value2)) {
return 0 === ($value1 % $value2);
}
if (!$remainder = fmod($value1, $value2)) {
return true;
}
if (\is_float($value2) && \INF !== $value2) {
$quotient = $value1 / $value2;
$rounded = round($quotient);
return \sprintf('%.12e', $quotient) === \sprintf('%.12e', $rounded);
}
return \sprintf('%.12e', $value2) === \sprintf('%.12e', $remainder);
}
protected function getErrorCode(): ?string
{
return DivisibleBy::NOT_DIVISIBLE_BY;
}
}

View File

@@ -0,0 +1,93 @@
<?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\Component\Validator\Constraints;
use Egulias\EmailValidator\EmailValidator as StrictEmailValidator;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\InvalidArgumentException;
use Symfony\Component\Validator\Exception\LogicException;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Email extends Constraint
{
public const VALIDATION_MODE_HTML5_ALLOW_NO_TLD = 'html5-allow-no-tld';
public const VALIDATION_MODE_HTML5 = 'html5';
public const VALIDATION_MODE_STRICT = 'strict';
/**
* @deprecated since Symfony 6.2, use VALIDATION_MODE_HTML5 instead
*/
public const VALIDATION_MODE_LOOSE = 'loose';
public const INVALID_FORMAT_ERROR = 'bd79c0ab-ddba-46cc-a703-a7a4b08de310';
public const VALIDATION_MODES = [
self::VALIDATION_MODE_HTML5_ALLOW_NO_TLD,
self::VALIDATION_MODE_HTML5,
self::VALIDATION_MODE_STRICT,
self::VALIDATION_MODE_LOOSE,
];
protected const ERROR_NAMES = [
self::INVALID_FORMAT_ERROR => 'INVALID_FORMAT_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $message = 'This value is not a valid email address.';
public $mode;
/** @var callable|null */
public $normalizer;
public function __construct(
?array $options = null,
?string $message = null,
?string $mode = null,
?callable $normalizer = null,
?array $groups = null,
mixed $payload = null,
) {
if (\is_array($options) && \array_key_exists('mode', $options) && !\in_array($options['mode'], self::VALIDATION_MODES, true)) {
throw new InvalidArgumentException('The "mode" parameter value is not valid.');
}
if (null !== $mode && !\in_array($mode, self::VALIDATION_MODES, true)) {
throw new InvalidArgumentException('The "mode" parameter value is not valid.');
}
parent::__construct($options, $groups, $payload);
$this->message = $message ?? $this->message;
$this->mode = $mode ?? $this->mode;
$this->normalizer = $normalizer ?? $this->normalizer;
if (self::VALIDATION_MODE_LOOSE === $this->mode) {
trigger_deprecation('symfony/validator', '6.2', 'The "%s" mode is deprecated. It will be removed in 7.0 and the default mode will be changed to "%s".', self::VALIDATION_MODE_LOOSE, self::VALIDATION_MODE_HTML5);
}
if (self::VALIDATION_MODE_STRICT === $this->mode && !class_exists(StrictEmailValidator::class)) {
throw new LogicException(\sprintf('The "egulias/email-validator" component is required to use the "%s" constraint in strict mode. Try running "composer require egulias/email-validator".', __CLASS__));
}
if (null !== $this->normalizer && !\is_callable($this->normalizer)) {
throw new InvalidArgumentException(\sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer)));
}
}
}

View File

@@ -0,0 +1,119 @@
<?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\Component\Validator\Constraints;
use Egulias\EmailValidator\EmailValidator as EguliasEmailValidator;
use Egulias\EmailValidator\Validation\EmailValidation;
use Egulias\EmailValidator\Validation\NoRFCWarningsValidation;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\InvalidArgumentException;
use Symfony\Component\Validator\Exception\LogicException;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class EmailValidator extends ConstraintValidator
{
private const PATTERN_HTML5_ALLOW_NO_TLD = '/^[a-zA-Z0-9.!#$%&\'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/D';
private const PATTERN_HTML5 = '/^[a-zA-Z0-9.!#$%&\'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$/D';
private const PATTERN_LOOSE = '/^.+\@\S+\.\S+$/D';
private const EMAIL_PATTERNS = [
Email::VALIDATION_MODE_LOOSE => self::PATTERN_LOOSE,
Email::VALIDATION_MODE_HTML5 => self::PATTERN_HTML5,
Email::VALIDATION_MODE_HTML5_ALLOW_NO_TLD => self::PATTERN_HTML5_ALLOW_NO_TLD,
];
private string $defaultMode;
public function __construct(string $defaultMode = Email::VALIDATION_MODE_LOOSE)
{
if (!\in_array($defaultMode, Email::VALIDATION_MODES, true)) {
throw new InvalidArgumentException('The "defaultMode" parameter value is not valid.');
}
if (Email::VALIDATION_MODE_LOOSE === $defaultMode) {
trigger_deprecation('symfony/validator', '6.2', 'The "%s" mode is deprecated. It will be removed in 7.0 and the default mode will be changed to "%s".', Email::VALIDATION_MODE_LOOSE, Email::VALIDATION_MODE_HTML5);
}
$this->defaultMode = $defaultMode;
}
/**
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof Email) {
throw new UnexpectedTypeException($constraint, Email::class);
}
if (null === $value || '' === $value) {
return;
}
if (!\is_scalar($value) && !$value instanceof \Stringable) {
throw new UnexpectedValueException($value, 'string');
}
$value = (string) $value;
if ('' === $value) {
return;
}
if (null !== $constraint->normalizer) {
$value = ($constraint->normalizer)($value);
}
if (null === $constraint->mode) {
if (Email::VALIDATION_MODE_STRICT === $this->defaultMode && !class_exists(EguliasEmailValidator::class)) {
throw new LogicException(\sprintf('The "egulias/email-validator" component is required to make the "%s" constraint default to strict mode. Try running "composer require egulias/email-validator".', Email::class));
}
$constraint->mode = $this->defaultMode;
}
if (!\in_array($constraint->mode, Email::VALIDATION_MODES, true)) {
throw new InvalidArgumentException(\sprintf('The "%s::$mode" parameter value is not valid.', get_debug_type($constraint)));
}
if (Email::VALIDATION_MODE_STRICT === $constraint->mode) {
$strictValidator = new EguliasEmailValidator();
if (interface_exists(EmailValidation::class) && !$strictValidator->isValid($value, new NoRFCWarningsValidation())) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(Email::INVALID_FORMAT_ERROR)
->addViolation();
return;
} elseif (!interface_exists(EmailValidation::class) && !$strictValidator->isValid($value, false, true)) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(Email::INVALID_FORMAT_ERROR)
->addViolation();
return;
}
} elseif (!preg_match(self::EMAIL_PATTERNS[$constraint->mode], $value)) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(Email::INVALID_FORMAT_ERROR)
->addViolation();
return;
}
}
}

View File

@@ -0,0 +1,43 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
/**
* Enables auto mapping.
*
* Using the annotations on a property has higher precedence than using it on a class,
* which has higher precedence than any configuration that might be defined outside the class.
*
* @Annotation
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::TARGET_CLASS)]
class EnableAutoMapping extends Constraint
{
public function __construct(?array $options = null)
{
if (\is_array($options) && \array_key_exists('groups', $options)) {
throw new ConstraintDefinitionException(\sprintf('The option "groups" is not supported by the constraint "%s".', __CLASS__));
}
parent::__construct($options);
}
public function getTargets(): string|array
{
return [self::PROPERTY_CONSTRAINT, self::CLASS_CONSTRAINT];
}
}

View File

@@ -0,0 +1,36 @@
<?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\Component\Validator\Constraints;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Daniel Holmes <daniel@danielholmes.org>
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class EqualTo extends AbstractComparison
{
public const NOT_EQUAL_ERROR = '478618a7-95ba-473d-9101-cabd45e49115';
protected const ERROR_NAMES = [
self::NOT_EQUAL_ERROR => 'NOT_EQUAL_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $message = 'This value should be equal to {{ compared_value }}.';
}

View File

@@ -0,0 +1,31 @@
<?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\Component\Validator\Constraints;
/**
* Validates values are equal (==).
*
* @author Daniel Holmes <daniel@danielholmes.org>
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class EqualToValidator extends AbstractComparisonValidator
{
protected function compareValues(mixed $value1, mixed $value2): bool
{
return $value1 == $value2;
}
protected function getErrorCode(): ?string
{
return EqualTo::NOT_EQUAL_ERROR;
}
}

View File

@@ -0,0 +1,30 @@
<?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\Component\Validator\Constraints;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
abstract class Existence extends Composite
{
public $constraints = [];
public function getDefaultOption(): ?string
{
return 'constraints';
}
protected function getCompositeOption(): string
{
return 'constraints';
}
}

View File

@@ -0,0 +1,90 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\ExpressionLanguage\Expression as ExpressionObject;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\LogicException;
/**
* @Annotation
* @Target({"CLASS", "PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)]
class Expression extends Constraint
{
public const EXPRESSION_FAILED_ERROR = '6b3befbc-2f01-4ddf-be21-b57898905284';
protected const ERROR_NAMES = [
self::EXPRESSION_FAILED_ERROR => 'EXPRESSION_FAILED_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $message = 'This value is not valid.';
public $expression;
public $values = [];
public bool $negate = true;
public function __construct(
string|ExpressionObject|array|null $expression,
?string $message = null,
?array $values = null,
?array $groups = null,
mixed $payload = null,
array $options = [],
?bool $negate = null,
) {
if (!class_exists(ExpressionLanguage::class)) {
throw new LogicException(\sprintf('The "symfony/expression-language" component is required to use the "%s" constraint. Try running "composer require symfony/expression-language".', __CLASS__));
}
if (\is_array($expression)) {
$options = array_merge($expression, $options);
} elseif (null !== $expression) {
$options['value'] = $expression;
}
parent::__construct($options, $groups, $payload);
$this->message = $message ?? $this->message;
$this->values = $values ?? $this->values;
$this->negate = $negate ?? $this->negate;
}
public function getDefaultOption(): ?string
{
return 'expression';
}
public function getRequiredOptions(): array
{
return ['expression'];
}
public function getTargets(): string|array
{
return [self::CLASS_CONSTRAINT, self::PROPERTY_CONSTRAINT];
}
public function validatedBy(): string
{
return 'validator.expression';
}
}

View File

@@ -0,0 +1,32 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\ExpressionLanguage\ExpressionFunction;
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
class ExpressionLanguageProvider implements ExpressionFunctionProviderInterface
{
public function getFunctions(): array
{
return [
new ExpressionFunction('is_valid', function (...$arguments) {
return \sprintf(
'0 === $context->getValidator()->inContext($context)->validate(%s)->getViolations()->count()',
implode(', ', $arguments)
);
}, function (array $variables, ...$arguments): bool {
return 0 === $variables['context']->getValidator()->inContext($variables['context'])->validate(...$arguments)->getViolations()->count();
}),
];
}
}

View File

@@ -0,0 +1,57 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
trigger_deprecation('symfony/validator', '6.1', 'The "%s" constraint is deprecated since symfony 6.1, use "ExpressionSyntax" instead.', ExpressionLanguageSyntax::class);
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Andrey Sevastianov <mrpkmail@gmail.com>
*
* @deprecated since symfony 6.1, use ExpressionSyntax instead
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class ExpressionLanguageSyntax extends Constraint
{
public const EXPRESSION_LANGUAGE_SYNTAX_ERROR = '1766a3f3-ff03-40eb-b053-ab7aa23d988a';
protected const ERROR_NAMES = [
self::EXPRESSION_LANGUAGE_SYNTAX_ERROR => 'EXPRESSION_LANGUAGE_SYNTAX_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $message = 'This value should be a valid expression.';
public $service;
public $allowedVariables;
public function __construct(?array $options = null, ?string $message = null, ?string $service = null, ?array $allowedVariables = null, ?array $groups = null, mixed $payload = null)
{
parent::__construct($options, $groups, $payload);
$this->message = $message ?? $this->message;
$this->service = $service ?? $this->service;
$this->allowedVariables = $allowedVariables ?? $this->allowedVariables;
}
public function validatedBy(): string
{
return $this->service ?? static::class.'Validator';
}
}

View File

@@ -0,0 +1,63 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Symfony\Component\ExpressionLanguage\SyntaxError;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
trigger_deprecation('symfony/validator', '6.1', 'The "%s" constraint is deprecated since symfony 6.1, use "ExpressionSyntaxValidator" instead.', ExpressionLanguageSyntaxValidator::class);
/**
* @author Andrey Sevastianov <mrpkmail@gmail.com>
*
* @deprecated since symfony 6.1, use ExpressionSyntaxValidator instead
*/
class ExpressionLanguageSyntaxValidator extends ConstraintValidator
{
private ?ExpressionLanguage $expressionLanguage;
public function __construct(?ExpressionLanguage $expressionLanguage = null)
{
if (!class_exists(ExpressionLanguage::class)) {
throw new \LogicException(\sprintf('The "%s" class requires the "ExpressionLanguage" component. Try running "composer require symfony/expression-language".', self::class));
}
$this->expressionLanguage = $expressionLanguage;
}
public function validate(mixed $expression, Constraint $constraint): void
{
if (!$constraint instanceof ExpressionLanguageSyntax) {
throw new UnexpectedTypeException($constraint, ExpressionLanguageSyntax::class);
}
if (!\is_string($expression)) {
throw new UnexpectedValueException($expression, 'string');
}
$this->expressionLanguage ??= new ExpressionLanguage();
try {
$this->expressionLanguage->lint($expression, $constraint->allowedVariables);
} catch (SyntaxError $exception) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ syntax_error }}', $this->formatValue($exception->getMessage()))
->setInvalidValue((string) $expression)
->setCode(ExpressionLanguageSyntax::EXPRESSION_LANGUAGE_SYNTAX_ERROR)
->addViolation();
}
}
}

View File

@@ -0,0 +1,48 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Andrey Sevastianov <mrpkmail@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class ExpressionSyntax extends Constraint
{
public const EXPRESSION_SYNTAX_ERROR = 'e219aa22-8b11-48ec-81a0-fc07cdb0e13f';
protected const ERROR_NAMES = [
self::EXPRESSION_SYNTAX_ERROR => 'EXPRESSION_SYNTAX_ERROR',
];
public $message = 'This value should be a valid expression.';
public $service;
public $allowedVariables;
public function __construct(?array $options = null, ?string $message = null, ?string $service = null, ?array $allowedVariables = null, ?array $groups = null, mixed $payload = null)
{
parent::__construct($options, $groups, $payload);
$this->message = $message ?? $this->message;
$this->service = $service ?? $this->service;
$this->allowedVariables = $allowedVariables ?? $this->allowedVariables;
}
public function validatedBy(): string
{
return $this->service ?? static::class.'Validator';
}
}

View File

@@ -0,0 +1,59 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Symfony\Component\ExpressionLanguage\SyntaxError;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
/**
* @author Andrey Sevastianov <mrpkmail@gmail.com>
*/
class ExpressionSyntaxValidator extends ConstraintValidator
{
private ?ExpressionLanguage $expressionLanguage;
public function __construct(?ExpressionLanguage $expressionLanguage = null)
{
$this->expressionLanguage = $expressionLanguage;
}
public function validate(mixed $expression, Constraint $constraint): void
{
if (!$constraint instanceof ExpressionSyntax) {
throw new UnexpectedTypeException($constraint, ExpressionSyntax::class);
}
if (null === $expression || '' === $expression) {
return;
}
if (!\is_string($expression)) {
throw new UnexpectedValueException($expression, 'string');
}
$this->expressionLanguage ??= new ExpressionLanguage();
try {
$this->expressionLanguage->lint($expression, $constraint->allowedVariables);
} catch (SyntaxError $exception) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ syntax_error }}', $this->formatValue($exception->getMessage()))
->setInvalidValue((string) $expression)
->setCode(ExpressionSyntax::EXPRESSION_SYNTAX_ERROR)
->addViolation();
}
}
}

View File

@@ -0,0 +1,65 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
/**
* @author Fabien Potencier <fabien@symfony.com>
* @author Bernhard Schussek <bschussek@symfony.com>
*/
class ExpressionValidator extends ConstraintValidator
{
private ExpressionLanguage $expressionLanguage;
public function __construct(?ExpressionLanguage $expressionLanguage = null)
{
if ($expressionLanguage) {
$this->expressionLanguage = $expressionLanguage;
}
}
/**
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof Expression) {
throw new UnexpectedTypeException($constraint, Expression::class);
}
$variables = $constraint->values;
$variables['value'] = $value;
$variables['this'] = $this->context->getObject();
$variables['context'] = $this->context;
if ($constraint->negate xor $this->getExpressionLanguage()->evaluate($constraint->expression, $variables)) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value, self::OBJECT_TO_STRING))
->setCode(Expression::EXPRESSION_FAILED_ERROR)
->addViolation();
}
}
private function getExpressionLanguage(): ExpressionLanguage
{
if (!isset($this->expressionLanguage)) {
$this->expressionLanguage = new ExpressionLanguage();
$this->expressionLanguage->registerProvider(new ExpressionLanguageProvider());
}
return $this->expressionLanguage;
}
}

View File

@@ -0,0 +1,186 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @property int $maxSize
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class File extends Constraint
{
// Check the Image constraint for clashes if adding new constants here
public const NOT_FOUND_ERROR = 'd2a3fb6e-7ddc-4210-8fbf-2ab345ce1998';
public const NOT_READABLE_ERROR = 'c20c92a4-5bfa-4202-9477-28e800e0f6ff';
public const EMPTY_ERROR = '5d743385-9775-4aa5-8ff5-495fb1e60137';
public const TOO_LARGE_ERROR = 'df8637af-d466-48c6-a59d-e7126250a654';
public const INVALID_MIME_TYPE_ERROR = '744f00bc-4389-4c74-92de-9a43cde55534';
public const INVALID_EXTENSION_ERROR = 'c8c7315c-6186-4719-8b71-5659e16bdcb7';
public const FILENAME_TOO_LONG = 'e5706483-91a8-49d8-9a59-5e81a3c634a8';
protected const ERROR_NAMES = [
self::NOT_FOUND_ERROR => 'NOT_FOUND_ERROR',
self::NOT_READABLE_ERROR => 'NOT_READABLE_ERROR',
self::EMPTY_ERROR => 'EMPTY_ERROR',
self::TOO_LARGE_ERROR => 'TOO_LARGE_ERROR',
self::INVALID_MIME_TYPE_ERROR => 'INVALID_MIME_TYPE_ERROR',
self::INVALID_EXTENSION_ERROR => 'INVALID_EXTENSION_ERROR',
self::FILENAME_TOO_LONG => 'FILENAME_TOO_LONG',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $binaryFormat;
public $mimeTypes = [];
public ?int $filenameMaxLength = null;
public array|string|null $extensions = [];
public $notFoundMessage = 'The file could not be found.';
public $notReadableMessage = 'The file is not readable.';
public $maxSizeMessage = 'The file is too large ({{ size }} {{ suffix }}). Allowed maximum size is {{ limit }} {{ suffix }}.';
public $mimeTypesMessage = 'The mime type of the file is invalid ({{ type }}). Allowed mime types are {{ types }}.';
public string $extensionsMessage = 'The extension of the file is invalid ({{ extension }}). Allowed extensions are {{ extensions }}.';
public $disallowEmptyMessage = 'An empty file is not allowed.';
public $filenameTooLongMessage = 'The filename is too long. It should have {{ filename_max_length }} character or less.|The filename is too long. It should have {{ filename_max_length }} characters or less.';
public $uploadIniSizeErrorMessage = 'The file is too large. Allowed maximum size is {{ limit }} {{ suffix }}.';
public $uploadFormSizeErrorMessage = 'The file is too large.';
public $uploadPartialErrorMessage = 'The file was only partially uploaded.';
public $uploadNoFileErrorMessage = 'No file was uploaded.';
public $uploadNoTmpDirErrorMessage = 'No temporary folder was configured in php.ini.';
public $uploadCantWriteErrorMessage = 'Cannot write temporary file to disk.';
public $uploadExtensionErrorMessage = 'A PHP extension caused the upload to fail.';
public $uploadErrorMessage = 'The file could not be uploaded.';
protected $maxSize;
/**
* @param array<string|string[]>|string $extensions
*/
public function __construct(
?array $options = null,
int|string|null $maxSize = null,
?bool $binaryFormat = null,
array|string|null $mimeTypes = null,
?int $filenameMaxLength = null,
?string $notFoundMessage = null,
?string $notReadableMessage = null,
?string $maxSizeMessage = null,
?string $mimeTypesMessage = null,
?string $disallowEmptyMessage = null,
?string $filenameTooLongMessage = null,
?string $uploadIniSizeErrorMessage = null,
?string $uploadFormSizeErrorMessage = null,
?string $uploadPartialErrorMessage = null,
?string $uploadNoFileErrorMessage = null,
?string $uploadNoTmpDirErrorMessage = null,
?string $uploadCantWriteErrorMessage = null,
?string $uploadExtensionErrorMessage = null,
?string $uploadErrorMessage = null,
?array $groups = null,
mixed $payload = null,
array|string|null $extensions = null,
?string $extensionsMessage = null,
) {
parent::__construct($options, $groups, $payload);
$this->maxSize = $maxSize ?? $this->maxSize;
$this->binaryFormat = $binaryFormat ?? $this->binaryFormat;
$this->mimeTypes = $mimeTypes ?? $this->mimeTypes;
$this->filenameMaxLength = $filenameMaxLength ?? $this->filenameMaxLength;
$this->extensions = $extensions ?? $this->extensions;
$this->notFoundMessage = $notFoundMessage ?? $this->notFoundMessage;
$this->notReadableMessage = $notReadableMessage ?? $this->notReadableMessage;
$this->maxSizeMessage = $maxSizeMessage ?? $this->maxSizeMessage;
$this->mimeTypesMessage = $mimeTypesMessage ?? $this->mimeTypesMessage;
$this->extensionsMessage = $extensionsMessage ?? $this->extensionsMessage;
$this->disallowEmptyMessage = $disallowEmptyMessage ?? $this->disallowEmptyMessage;
$this->filenameTooLongMessage = $filenameTooLongMessage ?? $this->filenameTooLongMessage;
$this->uploadIniSizeErrorMessage = $uploadIniSizeErrorMessage ?? $this->uploadIniSizeErrorMessage;
$this->uploadFormSizeErrorMessage = $uploadFormSizeErrorMessage ?? $this->uploadFormSizeErrorMessage;
$this->uploadPartialErrorMessage = $uploadPartialErrorMessage ?? $this->uploadPartialErrorMessage;
$this->uploadNoFileErrorMessage = $uploadNoFileErrorMessage ?? $this->uploadNoFileErrorMessage;
$this->uploadNoTmpDirErrorMessage = $uploadNoTmpDirErrorMessage ?? $this->uploadNoTmpDirErrorMessage;
$this->uploadCantWriteErrorMessage = $uploadCantWriteErrorMessage ?? $this->uploadCantWriteErrorMessage;
$this->uploadExtensionErrorMessage = $uploadExtensionErrorMessage ?? $this->uploadExtensionErrorMessage;
$this->uploadErrorMessage = $uploadErrorMessage ?? $this->uploadErrorMessage;
if (null !== $this->maxSize) {
$this->normalizeBinaryFormat($this->maxSize);
}
}
/**
* @return void
*/
public function __set(string $option, mixed $value)
{
if ('maxSize' === $option) {
$this->normalizeBinaryFormat($value);
return;
}
parent::__set($option, $value);
}
public function __get(string $option): mixed
{
if ('maxSize' === $option) {
return $this->maxSize;
}
return parent::__get($option);
}
public function __isset(string $option): bool
{
if ('maxSize' === $option) {
return true;
}
return parent::__isset($option);
}
private function normalizeBinaryFormat(int|string $maxSize): void
{
$factors = [
'k' => 1000,
'ki' => 1 << 10,
'm' => 1000 * 1000,
'mi' => 1 << 20,
'g' => 1000 * 1000 * 1000,
'gi' => 1 << 30,
];
if (ctype_digit((string) $maxSize)) {
$this->maxSize = (int) $maxSize;
$this->binaryFormat ??= false;
} elseif (preg_match('/^(\d++)('.implode('|', array_keys($factors)).')$/i', $maxSize, $matches)) {
$this->maxSize = $matches[1] * $factors[$unit = strtolower($matches[2])];
$this->binaryFormat ??= 2 === \strlen($unit);
} else {
throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid maximum size.', $maxSize));
}
}
}

View File

@@ -0,0 +1,309 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\HttpFoundation\File\File as FileObject;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Mime\MimeTypes;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\LogicException;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class FileValidator extends ConstraintValidator
{
public const KB_BYTES = 1000;
public const MB_BYTES = 1000000;
public const KIB_BYTES = 1024;
public const MIB_BYTES = 1048576;
private const SUFFICES = [
1 => 'bytes',
self::KB_BYTES => 'kB',
self::MB_BYTES => 'MB',
self::KIB_BYTES => 'KiB',
self::MIB_BYTES => 'MiB',
];
/**
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof File) {
throw new UnexpectedTypeException($constraint, File::class);
}
if (null === $value || '' === $value) {
return;
}
if ($value instanceof UploadedFile && !$value->isValid()) {
switch ($value->getError()) {
case \UPLOAD_ERR_INI_SIZE:
$iniLimitSize = UploadedFile::getMaxFilesize();
if ($constraint->maxSize && $constraint->maxSize < $iniLimitSize) {
$limitInBytes = $constraint->maxSize;
$binaryFormat = $constraint->binaryFormat;
} else {
$limitInBytes = $iniLimitSize;
$binaryFormat = $constraint->binaryFormat ?? true;
}
[, $limitAsString, $suffix] = $this->factorizeSizes(0, $limitInBytes, $binaryFormat);
$this->context->buildViolation($constraint->uploadIniSizeErrorMessage)
->setParameter('{{ limit }}', $limitAsString)
->setParameter('{{ suffix }}', $suffix)
->setCode((string) \UPLOAD_ERR_INI_SIZE)
->addViolation();
return;
case \UPLOAD_ERR_FORM_SIZE:
$this->context->buildViolation($constraint->uploadFormSizeErrorMessage)
->setCode((string) \UPLOAD_ERR_FORM_SIZE)
->addViolation();
return;
case \UPLOAD_ERR_PARTIAL:
$this->context->buildViolation($constraint->uploadPartialErrorMessage)
->setCode((string) \UPLOAD_ERR_PARTIAL)
->addViolation();
return;
case \UPLOAD_ERR_NO_FILE:
$this->context->buildViolation($constraint->uploadNoFileErrorMessage)
->setCode((string) \UPLOAD_ERR_NO_FILE)
->addViolation();
return;
case \UPLOAD_ERR_NO_TMP_DIR:
$this->context->buildViolation($constraint->uploadNoTmpDirErrorMessage)
->setCode((string) \UPLOAD_ERR_NO_TMP_DIR)
->addViolation();
return;
case \UPLOAD_ERR_CANT_WRITE:
$this->context->buildViolation($constraint->uploadCantWriteErrorMessage)
->setCode((string) \UPLOAD_ERR_CANT_WRITE)
->addViolation();
return;
case \UPLOAD_ERR_EXTENSION:
$this->context->buildViolation($constraint->uploadExtensionErrorMessage)
->setCode((string) \UPLOAD_ERR_EXTENSION)
->addViolation();
return;
default:
$this->context->buildViolation($constraint->uploadErrorMessage)
->setCode((string) $value->getError())
->addViolation();
return;
}
}
if (!\is_scalar($value) && !$value instanceof FileObject && !$value instanceof \Stringable) {
throw new UnexpectedValueException($value, 'string');
}
$path = $value instanceof FileObject ? $value->getPathname() : (string) $value;
if (!is_file($path)) {
$this->context->buildViolation($constraint->notFoundMessage)
->setParameter('{{ file }}', $this->formatValue($path))
->setCode(File::NOT_FOUND_ERROR)
->addViolation();
return;
}
if (!is_readable($path)) {
$this->context->buildViolation($constraint->notReadableMessage)
->setParameter('{{ file }}', $this->formatValue($path))
->setCode(File::NOT_READABLE_ERROR)
->addViolation();
return;
}
$sizeInBytes = filesize($path);
$basename = $value instanceof UploadedFile ? $value->getClientOriginalName() : basename($path);
if ($constraint->filenameMaxLength && $constraint->filenameMaxLength < $filenameLength = \strlen($basename)) {
$this->context->buildViolation($constraint->filenameTooLongMessage)
->setParameter('{{ filename_max_length }}', $this->formatValue($constraint->filenameMaxLength))
->setCode(File::FILENAME_TOO_LONG)
->setPlural($constraint->filenameMaxLength)
->addViolation();
return;
}
if (0 === $sizeInBytes) {
$this->context->buildViolation($constraint->disallowEmptyMessage)
->setParameter('{{ file }}', $this->formatValue($path))
->setParameter('{{ name }}', $this->formatValue($basename))
->setCode(File::EMPTY_ERROR)
->addViolation();
return;
}
if ($constraint->maxSize) {
$limitInBytes = $constraint->maxSize;
if ($sizeInBytes > $limitInBytes) {
[$sizeAsString, $limitAsString, $suffix] = $this->factorizeSizes($sizeInBytes, $limitInBytes, $constraint->binaryFormat);
$this->context->buildViolation($constraint->maxSizeMessage)
->setParameter('{{ file }}', $this->formatValue($path))
->setParameter('{{ size }}', $sizeAsString)
->setParameter('{{ limit }}', $limitAsString)
->setParameter('{{ suffix }}', $suffix)
->setParameter('{{ name }}', $this->formatValue($basename))
->setCode(File::TOO_LARGE_ERROR)
->addViolation();
return;
}
}
$mimeTypes = (array) $constraint->mimeTypes;
if ($constraint->extensions) {
$fileExtension = strtolower(pathinfo($basename, \PATHINFO_EXTENSION));
$found = false;
$normalizedExtensions = [];
foreach ((array) $constraint->extensions as $k => $v) {
if (!\is_string($k)) {
$k = $v;
$v = null;
}
$normalizedExtensions[] = $k;
if ($fileExtension !== $k) {
continue;
}
$found = true;
if (null === $v) {
if (!class_exists(MimeTypes::class)) {
throw new LogicException('You cannot validate the mime-type of files as the Mime component is not installed. Try running "composer require symfony/mime".');
}
$mimeTypesHelper = MimeTypes::getDefault();
$v = $mimeTypesHelper->getMimeTypes($k);
}
$mimeTypes = $mimeTypes ? array_intersect($v, $mimeTypes) : (array) $v;
break;
}
if (!$found) {
$this->context->buildViolation($constraint->extensionsMessage)
->setParameter('{{ file }}', $this->formatValue($path))
->setParameter('{{ extension }}', $this->formatValue($fileExtension))
->setParameter('{{ extensions }}', $this->formatValues($normalizedExtensions))
->setParameter('{{ name }}', $this->formatValue($basename))
->setCode(File::INVALID_EXTENSION_ERROR)
->addViolation();
}
}
if ($mimeTypes) {
if ($value instanceof FileObject) {
$mime = $value->getMimeType();
} elseif (isset($mimeTypesHelper) || class_exists(MimeTypes::class)) {
$mime = ($mimeTypesHelper ?? MimeTypes::getDefault())->guessMimeType($path);
} elseif (!class_exists(FileObject::class)) {
throw new LogicException('You cannot validate the mime-type of files as the Mime component is not installed. Try running "composer require symfony/mime".');
} else {
$mime = (new FileObject($value))->getMimeType();
}
foreach ($mimeTypes as $mimeType) {
if ($mimeType === $mime) {
return;
}
if ($discrete = strstr($mimeType, '/*', true)) {
if (strstr($mime, '/', true) === $discrete) {
return;
}
}
}
$this->context->buildViolation($constraint->mimeTypesMessage)
->setParameter('{{ file }}', $this->formatValue($path))
->setParameter('{{ type }}', $this->formatValue($mime))
->setParameter('{{ types }}', $this->formatValues($mimeTypes))
->setParameter('{{ name }}', $this->formatValue($basename))
->setCode(File::INVALID_MIME_TYPE_ERROR)
->addViolation();
}
}
private static function moreDecimalsThan(string $double, int $numberOfDecimals): bool
{
return \strlen($double) > \strlen(round($double, $numberOfDecimals));
}
/**
* Convert the limit to the smallest possible number
* (i.e. try "MB", then "kB", then "bytes").
*/
private function factorizeSizes(int $size, int|float $limit, bool $binaryFormat): array
{
if ($binaryFormat) {
$coef = self::MIB_BYTES;
$coefFactor = self::KIB_BYTES;
} else {
$coef = self::MB_BYTES;
$coefFactor = self::KB_BYTES;
}
// If $limit < $coef, $limitAsString could be < 1 with less than 3 decimals.
// In this case, we would end up displaying an allowed size < 1 (eg: 0.1 MB).
// It looks better to keep on factorizing (to display 100 kB for example).
while ($limit < $coef) {
$coef /= $coefFactor;
}
$limitAsString = (string) ($limit / $coef);
// Restrict the limit to 2 decimals (without rounding! we
// need the precise value)
while (self::moreDecimalsThan($limitAsString, 2)) {
$coef /= $coefFactor;
$limitAsString = (string) ($limit / $coef);
}
// Convert size to the same measure, but round to 2 decimals
$sizeAsString = (string) round($size / $coef, 2);
// If the size and limit produce the same string output
// (due to rounding), reduce the coefficient
while ($sizeAsString === $limitAsString) {
$coef /= $coefFactor;
$limitAsString = (string) ($limit / $coef);
$sizeAsString = (string) round($size / $coef, 2);
}
return [$sizeAsString, $limitAsString, self::SUFFICES[$coef]];
}
}

View File

@@ -0,0 +1,36 @@
<?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\Component\Validator\Constraints;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Daniel Holmes <daniel@danielholmes.org>
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class GreaterThan extends AbstractComparison
{
public const TOO_LOW_ERROR = '778b7ae0-84d3-481a-9dec-35fdb64b1d78';
protected const ERROR_NAMES = [
self::TOO_LOW_ERROR => 'TOO_LOW_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $message = 'This value should be greater than {{ compared_value }}.';
}

View File

@@ -0,0 +1,36 @@
<?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\Component\Validator\Constraints;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Daniel Holmes <daniel@danielholmes.org>
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class GreaterThanOrEqual extends AbstractComparison
{
public const TOO_LOW_ERROR = 'ea4e51d1-3342-48bd-87f1-9e672cd90cad';
protected const ERROR_NAMES = [
self::TOO_LOW_ERROR => 'TOO_LOW_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $message = 'This value should be greater than or equal to {{ compared_value }}.';
}

View File

@@ -0,0 +1,31 @@
<?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\Component\Validator\Constraints;
/**
* Validates values are greater than or equal to the previous (>=).
*
* @author Daniel Holmes <daniel@danielholmes.org>
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class GreaterThanOrEqualValidator extends AbstractComparisonValidator
{
protected function compareValues(mixed $value1, mixed $value2): bool
{
return null === $value2 || $value1 >= $value2;
}
protected function getErrorCode(): ?string
{
return GreaterThanOrEqual::TOO_LOW_ERROR;
}
}

View File

@@ -0,0 +1,31 @@
<?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\Component\Validator\Constraints;
/**
* Validates values are greater than the previous (>).
*
* @author Daniel Holmes <daniel@danielholmes.org>
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class GreaterThanValidator extends AbstractComparisonValidator
{
protected function compareValues(mixed $value1, mixed $value2): bool
{
return null === $value2 || $value1 > $value2;
}
protected function getErrorCode(): ?string
{
return GreaterThan::TOO_LOW_ERROR;
}
}

View File

@@ -0,0 +1,87 @@
<?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\Component\Validator\Constraints;
/**
* A sequence of validation groups.
*
* When validating a group sequence, each group will only be validated if all
* of the previous groups in the sequence succeeded. For example:
*
* $validator->validate($address, null, new GroupSequence(['Basic', 'Strict']));
*
* In the first step, all constraints that belong to the group "Basic" will be
* validated. If none of the constraints fail, the validator will then validate
* the constraints in group "Strict". This is useful, for example, if "Strict"
* contains expensive checks that require a lot of CPU or slow, external
* services. You usually don't want to run expensive checks if any of the cheap
* checks fail.
*
* When adding metadata to a class, you can override the "Default" group of
* that class with a group sequence:
* #[GroupSequence(['Address', 'Strict'])]
* class Address
* {
* // ...
* }
*
* Whenever you validate that object in the "Default" group, the group sequence
* will be validated:
*
* $validator->validate($address);
*
* If you want to execute the constraints of the "Default" group for a class
* with an overridden default group, pass the class name as group name instead:
*
* $validator->validate($address, null, "Address")
*
* @Annotation
* @Target({"CLASS", "ANNOTATION"})
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_CLASS)]
class GroupSequence
{
/**
* The groups in the sequence.
*
* @var array<int, string|string[]|GroupSequence>
*/
public $groups;
/**
* The group in which cascaded objects are validated when validating
* this sequence.
*
* By default, cascaded objects are validated in each of the groups of
* the sequence.
*
* If a class has a group sequence attached, that sequence replaces the
* "Default" group. When validating that class in the "Default" group, the
* group sequence is used instead, but still the "Default" group should be
* cascaded to other objects.
*
* @var string|GroupSequence
*/
public $cascadedGroup;
/**
* Creates a new group sequence.
*
* @param array<string|string[]|GroupSequence> $groups The groups in the sequence
*/
public function __construct(array $groups)
{
$this->groups = $groups['value'] ?? $groups;
}
}

View File

@@ -0,0 +1,33 @@
<?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\Component\Validator\Constraints;
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
use Symfony\Component\Validator\Attribute\HasNamedArguments;
/**
* Attribute to define a group sequence provider.
*
* @Annotation
* @NamedArgumentConstructor
* @Target({"CLASS", "ANNOTATION"})
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_CLASS)]
class GroupSequenceProvider
{
#[HasNamedArguments]
public function __construct(public ?string $provider = null)
{
}
}

View File

@@ -0,0 +1,51 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Dmitrii Poddubnyi <dpoddubny@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Hostname extends Constraint
{
public const INVALID_HOSTNAME_ERROR = '7057ffdb-0af4-4f7e-bd5e-e9acfa6d7a2d';
protected const ERROR_NAMES = [
self::INVALID_HOSTNAME_ERROR => 'INVALID_HOSTNAME_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $message = 'This value is not a valid hostname.';
public $requireTld = true;
public function __construct(
?array $options = null,
?string $message = null,
?bool $requireTld = null,
?array $groups = null,
mixed $payload = null,
) {
parent::__construct($options, $groups, $payload);
$this->message = $message ?? $this->message;
$this->requireTld = $requireTld ?? $this->requireTld;
}
}

View File

@@ -0,0 +1,72 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
/**
* @author Dmitrii Poddubnyi <dpoddubny@gmail.com>
*/
class HostnameValidator extends ConstraintValidator
{
/**
* https://tools.ietf.org/html/rfc2606.
*/
private const RESERVED_TLDS = [
'example',
'invalid',
'localhost',
'test',
];
/**
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof Hostname) {
throw new UnexpectedTypeException($constraint, Hostname::class);
}
if (null === $value || '' === $value) {
return;
}
if (!\is_scalar($value) && !$value instanceof \Stringable) {
throw new UnexpectedValueException($value, 'string');
}
$value = (string) $value;
if ('' === $value) {
return;
}
if (!$this->isValid($value) || ($constraint->requireTld && !$this->hasValidTld($value))) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(Hostname::INVALID_HOSTNAME_ERROR)
->addViolation();
}
}
private function isValid(string $domain): bool
{
return false !== filter_var($domain, \FILTER_VALIDATE_DOMAIN, \FILTER_FLAG_HOSTNAME);
}
private function hasValidTld(string $domain): bool
{
return str_contains($domain, '.') && !\in_array(substr($domain, strrpos($domain, '.') + 1), self::RESERVED_TLDS, true);
}
}

View File

@@ -0,0 +1,54 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Manuel Reinhard <manu@sprain.ch>
* @author Michael Schummel
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Iban extends Constraint
{
public const INVALID_COUNTRY_CODE_ERROR = 'de78ee2c-bd50-44e2-aec8-3d8228aeadb9';
public const INVALID_CHARACTERS_ERROR = '8d3d85e4-784f-4719-a5bc-d9e40d45a3a5';
public const CHECKSUM_FAILED_ERROR = 'b9401321-f9bf-4dcb-83c1-f31094440795';
public const INVALID_FORMAT_ERROR = 'c8d318f1-2ecc-41ba-b983-df70d225cf5a';
public const NOT_SUPPORTED_COUNTRY_CODE_ERROR = 'e2c259f3-4b46-48e6-b72e-891658158ec8';
protected const ERROR_NAMES = [
self::INVALID_COUNTRY_CODE_ERROR => 'INVALID_COUNTRY_CODE_ERROR',
self::INVALID_CHARACTERS_ERROR => 'INVALID_CHARACTERS_ERROR',
self::CHECKSUM_FAILED_ERROR => 'CHECKSUM_FAILED_ERROR',
self::INVALID_FORMAT_ERROR => 'INVALID_FORMAT_ERROR',
self::NOT_SUPPORTED_COUNTRY_CODE_ERROR => 'NOT_SUPPORTED_COUNTRY_CODE_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $message = 'This is not a valid International Bank Account Number (IBAN).';
public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null)
{
parent::__construct($options, $groups, $payload);
$this->message = $message ?? $this->message;
}
}

View File

@@ -0,0 +1,301 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
/**
* @author Manuel Reinhard <manu@sprain.ch>
* @author Michael Schummel
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class IbanValidator extends ConstraintValidator
{
/**
* IBAN country specific formats.
*
* The first 2 characters from an IBAN format are the two-character ISO country code.
* The following 2 characters represent the check digits calculated from the rest of the IBAN characters.
* The rest are up to thirty alphanumeric characters for
* a BBAN (Basic Bank Account Number) which has a fixed length per country and,
* included within it, a bank identifier with a fixed position and a fixed length per country
*
* @see Resources/bin/sync-iban-formats.php
* @see https://www.swift.com/swift-resource/11971/download?language=en
* @see https://en.wikipedia.org/wiki/International_Bank_Account_Number
*/
private const FORMATS = [
// auto-generated
'AD' => 'AD\d{2}\d{4}\d{4}[\dA-Z]{12}', // Andorra
'AE' => 'AE\d{2}\d{3}\d{16}', // United Arab Emirates (The)
'AL' => 'AL\d{2}\d{8}[\dA-Z]{16}', // Albania
'AO' => 'AO\d{2}\d{21}', // Angola
'AT' => 'AT\d{2}\d{5}\d{11}', // Austria
'AX' => 'FI\d{2}\d{3}\d{11}', // Finland
'AZ' => 'AZ\d{2}[A-Z]{4}[\dA-Z]{20}', // Azerbaijan
'BA' => 'BA\d{2}\d{3}\d{3}\d{8}\d{2}', // Bosnia and Herzegovina
'BE' => 'BE\d{2}\d{3}\d{7}\d{2}', // Belgium
'BF' => 'BF\d{2}[\dA-Z]{2}\d{22}', // Burkina Faso
'BG' => 'BG\d{2}[A-Z]{4}\d{4}\d{2}[\dA-Z]{8}', // Bulgaria
'BH' => 'BH\d{2}[A-Z]{4}[\dA-Z]{14}', // Bahrain
'BI' => 'BI\d{2}\d{5}\d{5}\d{11}\d{2}', // Burundi
'BJ' => 'BJ\d{2}[\dA-Z]{2}\d{22}', // Benin
'BL' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France
'BR' => 'BR\d{2}\d{8}\d{5}\d{10}[A-Z]{1}[\dA-Z]{1}', // Brazil
'BY' => 'BY\d{2}[\dA-Z]{4}\d{4}[\dA-Z]{16}', // Republic of Belarus
'CF' => 'CF\d{2}\d{23}', // Central African Republic
'CG' => 'CG\d{2}\d{23}', // Congo, Republic of the
'CH' => 'CH\d{2}\d{5}[\dA-Z]{12}', // Switzerland
'CI' => 'CI\d{2}[A-Z]{1}\d{23}', // Côte d'Ivoire
'CM' => 'CM\d{2}\d{23}', // Cameroon
'CR' => 'CR\d{2}\d{4}\d{14}', // Costa Rica
'CV' => 'CV\d{2}\d{21}', // Cabo Verde
'CY' => 'CY\d{2}\d{3}\d{5}[\dA-Z]{16}', // Cyprus
'CZ' => 'CZ\d{2}\d{4}\d{6}\d{10}', // Czechia
'DE' => 'DE\d{2}\d{8}\d{10}', // Germany
'DJ' => 'DJ\d{2}\d{5}\d{5}\d{11}\d{2}', // Djibouti
'DK' => 'DK\d{2}\d{4}\d{9}\d{1}', // Denmark
'DO' => 'DO\d{2}[\dA-Z]{4}\d{20}', // Dominican Republic
'DZ' => 'DZ\d{2}\d{22}', // Algeria
'EE' => 'EE\d{2}\d{2}\d{14}', // Estonia
'EG' => 'EG\d{2}\d{4}\d{4}\d{17}', // Egypt
'ES' => 'ES\d{2}\d{4}\d{4}\d{1}\d{1}\d{10}', // Spain
'FI' => 'FI\d{2}\d{3}\d{11}', // Finland
'FK' => 'FK\d{2}[A-Z]{2}\d{12}', // Falkland Islands
'FO' => 'FO\d{2}\d{4}\d{9}\d{1}', // Faroe Islands
'FR' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France
'GA' => 'GA\d{2}\d{23}', // Gabon
'GB' => 'GB\d{2}[A-Z]{4}\d{6}\d{8}', // United Kingdom
'GE' => 'GE\d{2}[A-Z]{2}\d{16}', // Georgia
'GF' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France
'GG' => 'GB\d{2}[A-Z]{4}\d{6}\d{8}', // United Kingdom
'GI' => 'GI\d{2}[A-Z]{4}[\dA-Z]{15}', // Gibraltar
'GL' => 'GL\d{2}\d{4}\d{9}\d{1}', // Greenland
'GP' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France
'GQ' => 'GQ\d{2}\d{23}', // Equatorial Guinea
'GR' => 'GR\d{2}\d{3}\d{4}[\dA-Z]{16}', // Greece
'GT' => 'GT\d{2}[\dA-Z]{4}[\dA-Z]{20}', // Guatemala
'GW' => 'GW\d{2}[\dA-Z]{2}\d{19}', // Guinea-Bissau
'HN' => 'HN\d{2}[A-Z]{4}\d{20}', // Honduras
'HR' => 'HR\d{2}\d{7}\d{10}', // Croatia
'HU' => 'HU\d{2}\d{3}\d{4}\d{1}\d{15}\d{1}', // Hungary
'IE' => 'IE\d{2}[A-Z]{4}\d{6}\d{8}', // Ireland
'IL' => 'IL\d{2}\d{3}\d{3}\d{13}', // Israel
'IM' => 'GB\d{2}[A-Z]{4}\d{6}\d{8}', // United Kingdom
'IQ' => 'IQ\d{2}[A-Z]{4}\d{3}\d{12}', // Iraq
'IR' => 'IR\d{2}\d{22}', // Iran
'IS' => 'IS\d{2}\d{4}\d{2}\d{6}\d{10}', // Iceland
'IT' => 'IT\d{2}[A-Z]{1}\d{5}\d{5}[\dA-Z]{12}', // Italy
'JE' => 'GB\d{2}[A-Z]{4}\d{6}\d{8}', // United Kingdom
'JO' => 'JO\d{2}[A-Z]{4}\d{4}[\dA-Z]{18}', // Jordan
'KM' => 'KM\d{2}\d{23}', // Comoros
'KW' => 'KW\d{2}[A-Z]{4}[\dA-Z]{22}', // Kuwait
'KZ' => 'KZ\d{2}\d{3}[\dA-Z]{13}', // Kazakhstan
'LB' => 'LB\d{2}\d{4}[\dA-Z]{20}', // Lebanon
'LC' => 'LC\d{2}[A-Z]{4}[\dA-Z]{24}', // Saint Lucia
'LI' => 'LI\d{2}\d{5}[\dA-Z]{12}', // Liechtenstein
'LT' => 'LT\d{2}\d{5}\d{11}', // Lithuania
'LU' => 'LU\d{2}\d{3}[\dA-Z]{13}', // Luxembourg
'LV' => 'LV\d{2}[A-Z]{4}[\dA-Z]{13}', // Latvia
'LY' => 'LY\d{2}\d{3}\d{3}\d{15}', // Libya
'MA' => 'MA\d{2}\d{24}', // Morocco
'MC' => 'MC\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // Monaco
'MD' => 'MD\d{2}[\dA-Z]{2}[\dA-Z]{18}', // Moldova
'ME' => 'ME\d{2}\d{3}\d{13}\d{2}', // Montenegro
'MF' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France
'MG' => 'MG\d{2}\d{23}', // Madagascar
'MK' => 'MK\d{2}\d{3}[\dA-Z]{10}\d{2}', // Macedonia
'ML' => 'ML\d{2}[\dA-Z]{2}\d{22}', // Mali
'MN' => 'MN\d{2}\d{4}\d{12}', // Mongolia
'MQ' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France
'MR' => 'MR\d{2}\d{5}\d{5}\d{11}\d{2}', // Mauritania
'MT' => 'MT\d{2}[A-Z]{4}\d{5}[\dA-Z]{18}', // Malta
'MU' => 'MU\d{2}[A-Z]{4}\d{2}\d{2}\d{12}\d{3}[A-Z]{3}', // Mauritius
'MZ' => 'MZ\d{2}\d{21}', // Mozambique
'NC' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France
'NE' => 'NE\d{2}[A-Z]{2}\d{22}', // Niger
'NI' => 'NI\d{2}[A-Z]{4}\d{20}', // Nicaragua
'NL' => 'NL\d{2}[A-Z]{4}\d{10}', // Netherlands (The)
'NO' => 'NO\d{2}\d{4}\d{6}\d{1}', // Norway
'OM' => 'OM\d{2}\d{3}[\dA-Z]{16}', // Oman
'PF' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France
'PK' => 'PK\d{2}[A-Z]{4}[\dA-Z]{16}', // Pakistan
'PL' => 'PL\d{2}\d{8}\d{16}', // Poland
'PM' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France
'PS' => 'PS\d{2}[A-Z]{4}[\dA-Z]{21}', // Palestine, State of
'PT' => 'PT\d{2}\d{4}\d{4}\d{11}\d{2}', // Portugal
'QA' => 'QA\d{2}[A-Z]{4}[\dA-Z]{21}', // Qatar
'RE' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France
'RO' => 'RO\d{2}[A-Z]{4}[\dA-Z]{16}', // Romania
'RS' => 'RS\d{2}\d{3}\d{13}\d{2}', // Serbia
'RU' => 'RU\d{2}\d{9}\d{5}[\dA-Z]{15}', // Russia
'SA' => 'SA\d{2}\d{2}[\dA-Z]{18}', // Saudi Arabia
'SC' => 'SC\d{2}[A-Z]{4}\d{2}\d{2}\d{16}[A-Z]{3}', // Seychelles
'SD' => 'SD\d{2}\d{2}\d{12}', // Sudan
'SE' => 'SE\d{2}\d{3}\d{16}\d{1}', // Sweden
'SI' => 'SI\d{2}\d{5}\d{8}\d{2}', // Slovenia
'SK' => 'SK\d{2}\d{4}\d{6}\d{10}', // Slovakia
'SM' => 'SM\d{2}[A-Z]{1}\d{5}\d{5}[\dA-Z]{12}', // San Marino
'SN' => 'SN\d{2}[A-Z]{2}\d{22}', // Senegal
'SO' => 'SO\d{2}\d{4}\d{3}\d{12}', // Somalia
'ST' => 'ST\d{2}\d{8}\d{11}\d{2}', // Sao Tome and Principe
'SV' => 'SV\d{2}[A-Z]{4}\d{20}', // El Salvador
'TD' => 'TD\d{2}\d{23}', // Chad
'TF' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France
'TG' => 'TG\d{2}[A-Z]{2}\d{22}', // Togo
'TL' => 'TL\d{2}\d{3}\d{14}\d{2}', // Timor-Leste
'TN' => 'TN\d{2}\d{2}\d{3}\d{13}\d{2}', // Tunisia
'TR' => 'TR\d{2}\d{5}\d{1}[\dA-Z]{16}', // Turkey
'UA' => 'UA\d{2}\d{6}[\dA-Z]{19}', // Ukraine
'VA' => 'VA\d{2}\d{3}\d{15}', // Vatican City State
'VG' => 'VG\d{2}[A-Z]{4}\d{16}', // Virgin Islands
'WF' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France
'XK' => 'XK\d{2}\d{4}\d{10}\d{2}', // Kosovo
'YE' => 'YE\d{2}[A-Z]{4}\d{4}[\dA-Z]{18}', // Yemen
'YT' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France
];
/**
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof Iban) {
throw new UnexpectedTypeException($constraint, Iban::class);
}
if (null === $value || '' === $value) {
return;
}
if (!\is_scalar($value) && !$value instanceof \Stringable) {
throw new UnexpectedValueException($value, 'string');
}
$value = (string) $value;
// Remove spaces and convert to uppercase
$canonicalized = str_replace(' ', '', strtoupper($value));
// The IBAN must contain only digits and characters...
if (!ctype_alnum($canonicalized)) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(Iban::INVALID_CHARACTERS_ERROR)
->addViolation();
return;
}
// ...start with a two-letter country code
$countryCode = substr($canonicalized, 0, 2);
if (!ctype_alpha($countryCode)) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(Iban::INVALID_COUNTRY_CODE_ERROR)
->addViolation();
return;
}
// ...have a format available
if (!\array_key_exists($countryCode, self::FORMATS)) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(Iban::NOT_SUPPORTED_COUNTRY_CODE_ERROR)
->addViolation();
return;
}
// ...and have a valid format
if (!preg_match('/^'.self::FORMATS[$countryCode].'$/', $canonicalized)
) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(Iban::INVALID_FORMAT_ERROR)
->addViolation();
return;
}
// Check digits should always between 2 and 98
// A ECBS document (https://www.ecbs.org/Download/EBS204_V3.PDF) replicates part of the ISO/IEC 7064:2003 standard as a method for generating check digits in the range 02 to 98.
$checkDigits = (int) substr($canonicalized, 2, 2);
if ($checkDigits < 2 || $checkDigits > 98) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(Iban::CHECKSUM_FAILED_ERROR)
->addViolation();
return;
}
// Move the first four characters to the end
// e.g. CH93 0076 2011 6238 5295 7
// -> 0076 2011 6238 5295 7 CH93
$canonicalized = substr($canonicalized, 4).substr($canonicalized, 0, 4);
// Convert all remaining letters to their ordinals
// The result is an integer, which is too large for PHP's int
// data type, so we store it in a string instead.
// e.g. 0076 2011 6238 5295 7 CH93
// -> 0076 2011 6238 5295 7 121893
$checkSum = self::toBigInt($canonicalized);
// Do a modulo-97 operation on the large integer
// We cannot use PHP's modulo operator, so we calculate the
// modulo step-wisely instead
if (1 !== self::bigModulo97($checkSum)) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(Iban::CHECKSUM_FAILED_ERROR)
->addViolation();
}
}
private static function toBigInt(string $string): string
{
$chars = str_split($string);
$bigInt = '';
foreach ($chars as $char) {
// Convert uppercase characters to ordinals, starting with 10 for "A"
if (ctype_upper($char)) {
$bigInt .= (\ord($char) - 55);
continue;
}
// Simply append digits
$bigInt .= $char;
}
return $bigInt;
}
private static function bigModulo97(string $bigInt): int
{
$parts = str_split($bigInt, 7);
$rest = 0;
foreach ($parts as $part) {
$rest = ($rest.$part) % 97;
}
return $rest;
}
}

View File

@@ -0,0 +1,36 @@
<?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\Component\Validator\Constraints;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Daniel Holmes <daniel@danielholmes.org>
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class IdenticalTo extends AbstractComparison
{
public const NOT_IDENTICAL_ERROR = '2a8cc50f-58a2-4536-875e-060a2ce69ed5';
protected const ERROR_NAMES = [
self::NOT_IDENTICAL_ERROR => 'NOT_IDENTICAL_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $message = 'This value should be identical to {{ compared_value_type }} {{ compared_value }}.';
}

View File

@@ -0,0 +1,31 @@
<?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\Component\Validator\Constraints;
/**
* Validates values are identical (===).
*
* @author Daniel Holmes <daniel@danielholmes.org>
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class IdenticalToValidator extends AbstractComparisonValidator
{
protected function compareValues(mixed $value1, mixed $value2): bool
{
return $value1 === $value2;
}
protected function getErrorCode(): ?string
{
return IdenticalTo::NOT_IDENTICAL_ERROR;
}
}

View File

@@ -0,0 +1,207 @@
<?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\Component\Validator\Constraints;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Benjamin Dulau <benjamin.dulau@gmail.com>
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Image extends File
{
public const SIZE_NOT_DETECTED_ERROR = '6d55c3f4-e58e-4fe3-91ee-74b492199956';
public const TOO_WIDE_ERROR = '7f87163d-878f-47f5-99ba-a8eb723a1ab2';
public const TOO_NARROW_ERROR = '9afbd561-4f90-4a27-be62-1780fc43604a';
public const TOO_HIGH_ERROR = '7efae81c-4877-47ba-aa65-d01ccb0d4645';
public const TOO_LOW_ERROR = 'aef0cb6a-c07f-4894-bc08-1781420d7b4c';
public const TOO_FEW_PIXEL_ERROR = '1b06b97d-ae48-474e-978f-038a74854c43';
public const TOO_MANY_PIXEL_ERROR = 'ee0804e8-44db-4eac-9775-be91aaf72ce1';
public const RATIO_TOO_BIG_ERROR = '70cafca6-168f-41c9-8c8c-4e47a52be643';
public const RATIO_TOO_SMALL_ERROR = '59b8c6ef-bcf2-4ceb-afff-4642ed92f12e';
public const SQUARE_NOT_ALLOWED_ERROR = '5d41425b-facb-47f7-a55a-de9fbe45cb46';
public const LANDSCAPE_NOT_ALLOWED_ERROR = '6f895685-7cf2-4d65-b3da-9029c5581d88';
public const PORTRAIT_NOT_ALLOWED_ERROR = '65608156-77da-4c79-a88c-02ef6d18c782';
public const CORRUPTED_IMAGE_ERROR = '5d4163f3-648f-4e39-87fd-cc5ea7aad2d1';
// Include the mapping from the base class
protected const ERROR_NAMES = [
self::NOT_FOUND_ERROR => 'NOT_FOUND_ERROR',
self::NOT_READABLE_ERROR => 'NOT_READABLE_ERROR',
self::EMPTY_ERROR => 'EMPTY_ERROR',
self::TOO_LARGE_ERROR => 'TOO_LARGE_ERROR',
self::INVALID_MIME_TYPE_ERROR => 'INVALID_MIME_TYPE_ERROR',
self::FILENAME_TOO_LONG => 'FILENAME_TOO_LONG',
self::SIZE_NOT_DETECTED_ERROR => 'SIZE_NOT_DETECTED_ERROR',
self::TOO_WIDE_ERROR => 'TOO_WIDE_ERROR',
self::TOO_NARROW_ERROR => 'TOO_NARROW_ERROR',
self::TOO_HIGH_ERROR => 'TOO_HIGH_ERROR',
self::TOO_LOW_ERROR => 'TOO_LOW_ERROR',
self::TOO_FEW_PIXEL_ERROR => 'TOO_FEW_PIXEL_ERROR',
self::TOO_MANY_PIXEL_ERROR => 'TOO_MANY_PIXEL_ERROR',
self::RATIO_TOO_BIG_ERROR => 'RATIO_TOO_BIG_ERROR',
self::RATIO_TOO_SMALL_ERROR => 'RATIO_TOO_SMALL_ERROR',
self::SQUARE_NOT_ALLOWED_ERROR => 'SQUARE_NOT_ALLOWED_ERROR',
self::LANDSCAPE_NOT_ALLOWED_ERROR => 'LANDSCAPE_NOT_ALLOWED_ERROR',
self::PORTRAIT_NOT_ALLOWED_ERROR => 'PORTRAIT_NOT_ALLOWED_ERROR',
self::CORRUPTED_IMAGE_ERROR => 'CORRUPTED_IMAGE_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $mimeTypes;
public $minWidth;
public $maxWidth;
public $maxHeight;
public $minHeight;
public $maxRatio;
public $minRatio;
public $minPixels;
public $maxPixels;
public $allowSquare = true;
public $allowLandscape = true;
public $allowPortrait = true;
public $detectCorrupted = false;
// The constant for a wrong MIME type is taken from the parent class.
public $mimeTypesMessage = 'This file is not a valid image.';
public $sizeNotDetectedMessage = 'The size of the image could not be detected.';
public $maxWidthMessage = 'The image width is too big ({{ width }}px). Allowed maximum width is {{ max_width }}px.';
public $minWidthMessage = 'The image width is too small ({{ width }}px). Minimum width expected is {{ min_width }}px.';
public $maxHeightMessage = 'The image height is too big ({{ height }}px). Allowed maximum height is {{ max_height }}px.';
public $minHeightMessage = 'The image height is too small ({{ height }}px). Minimum height expected is {{ min_height }}px.';
public $minPixelsMessage = 'The image has too few pixels ({{ pixels }} pixels). Minimum amount expected is {{ min_pixels }} pixels.';
public $maxPixelsMessage = 'The image has too many pixels ({{ pixels }} pixels). Maximum amount expected is {{ max_pixels }} pixels.';
public $maxRatioMessage = 'The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}.';
public $minRatioMessage = 'The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}.';
public $allowSquareMessage = 'The image is square ({{ width }}x{{ height }}px). Square images are not allowed.';
public $allowLandscapeMessage = 'The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed.';
public $allowPortraitMessage = 'The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed.';
public $corruptedMessage = 'The image file is corrupted.';
public function __construct(
?array $options = null,
int|string|null $maxSize = null,
?bool $binaryFormat = null,
?array $mimeTypes = null,
?int $filenameMaxLength = null,
?int $minWidth = null,
?int $maxWidth = null,
?int $maxHeight = null,
?int $minHeight = null,
int|float|null $maxRatio = null,
int|float|null $minRatio = null,
int|float|null $minPixels = null,
int|float|null $maxPixels = null,
?bool $allowSquare = null,
?bool $allowLandscape = null,
?bool $allowPortrait = null,
?bool $detectCorrupted = null,
?string $notFoundMessage = null,
?string $notReadableMessage = null,
?string $maxSizeMessage = null,
?string $mimeTypesMessage = null,
?string $disallowEmptyMessage = null,
?string $filenameTooLongMessage = null,
?string $uploadIniSizeErrorMessage = null,
?string $uploadFormSizeErrorMessage = null,
?string $uploadPartialErrorMessage = null,
?string $uploadNoFileErrorMessage = null,
?string $uploadNoTmpDirErrorMessage = null,
?string $uploadCantWriteErrorMessage = null,
?string $uploadExtensionErrorMessage = null,
?string $uploadErrorMessage = null,
?string $sizeNotDetectedMessage = null,
?string $maxWidthMessage = null,
?string $minWidthMessage = null,
?string $maxHeightMessage = null,
?string $minHeightMessage = null,
?string $minPixelsMessage = null,
?string $maxPixelsMessage = null,
?string $maxRatioMessage = null,
?string $minRatioMessage = null,
?string $allowSquareMessage = null,
?string $allowLandscapeMessage = null,
?string $allowPortraitMessage = null,
?string $corruptedMessage = null,
?array $groups = null,
mixed $payload = null,
array|string|null $extensions = null,
?string $extensionsMessage = null,
) {
parent::__construct(
$options,
$maxSize,
$binaryFormat,
$mimeTypes,
$filenameMaxLength,
$notFoundMessage,
$notReadableMessage,
$maxSizeMessage,
$mimeTypesMessage,
$disallowEmptyMessage,
$filenameTooLongMessage,
$uploadIniSizeErrorMessage,
$uploadFormSizeErrorMessage,
$uploadPartialErrorMessage,
$uploadNoFileErrorMessage,
$uploadNoTmpDirErrorMessage,
$uploadCantWriteErrorMessage,
$uploadExtensionErrorMessage,
$uploadErrorMessage,
$groups,
$payload,
$extensions,
$extensionsMessage,
);
$this->minWidth = $minWidth ?? $this->minWidth;
$this->maxWidth = $maxWidth ?? $this->maxWidth;
$this->maxHeight = $maxHeight ?? $this->maxHeight;
$this->minHeight = $minHeight ?? $this->minHeight;
$this->maxRatio = $maxRatio ?? $this->maxRatio;
$this->minRatio = $minRatio ?? $this->minRatio;
$this->minPixels = $minPixels ?? $this->minPixels;
$this->maxPixels = $maxPixels ?? $this->maxPixels;
$this->allowSquare = $allowSquare ?? $this->allowSquare;
$this->allowLandscape = $allowLandscape ?? $this->allowLandscape;
$this->allowPortrait = $allowPortrait ?? $this->allowPortrait;
$this->detectCorrupted = $detectCorrupted ?? $this->detectCorrupted;
$this->sizeNotDetectedMessage = $sizeNotDetectedMessage ?? $this->sizeNotDetectedMessage;
$this->maxWidthMessage = $maxWidthMessage ?? $this->maxWidthMessage;
$this->minWidthMessage = $minWidthMessage ?? $this->minWidthMessage;
$this->maxHeightMessage = $maxHeightMessage ?? $this->maxHeightMessage;
$this->minHeightMessage = $minHeightMessage ?? $this->minHeightMessage;
$this->minPixelsMessage = $minPixelsMessage ?? $this->minPixelsMessage;
$this->maxPixelsMessage = $maxPixelsMessage ?? $this->maxPixelsMessage;
$this->maxRatioMessage = $maxRatioMessage ?? $this->maxRatioMessage;
$this->minRatioMessage = $minRatioMessage ?? $this->minRatioMessage;
$this->allowSquareMessage = $allowSquareMessage ?? $this->allowSquareMessage;
$this->allowLandscapeMessage = $allowLandscapeMessage ?? $this->allowLandscapeMessage;
$this->allowPortraitMessage = $allowPortraitMessage ?? $this->allowPortraitMessage;
$this->corruptedMessage = $corruptedMessage ?? $this->corruptedMessage;
if (null === $this->mimeTypes && [] === $this->extensions) {
$this->mimeTypes = 'image/*';
}
if (!\in_array('image/*', (array) $this->mimeTypes, true) && !\array_key_exists('mimeTypesMessage', $options ?? []) && null === $mimeTypesMessage) {
$this->mimeTypesMessage = 'The mime type of the file is invalid ({{ type }}). Allowed mime types are {{ types }}.';
}
}
}

View File

@@ -0,0 +1,235 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Exception\LogicException;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
/**
* Validates whether a value is a valid image file and is valid
* against minWidth, maxWidth, minHeight and maxHeight constraints.
*
* @author Benjamin Dulau <benjamin.dulau@gmail.com>
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class ImageValidator extends FileValidator
{
/**
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof Image) {
throw new UnexpectedTypeException($constraint, Image::class);
}
$violations = \count($this->context->getViolations());
parent::validate($value, $constraint);
$failed = \count($this->context->getViolations()) !== $violations;
if ($failed || null === $value || '' === $value) {
return;
}
if (null === $constraint->minWidth && null === $constraint->maxWidth
&& null === $constraint->minHeight && null === $constraint->maxHeight
&& null === $constraint->minPixels && null === $constraint->maxPixels
&& null === $constraint->minRatio && null === $constraint->maxRatio
&& $constraint->allowSquare && $constraint->allowLandscape && $constraint->allowPortrait
&& !$constraint->detectCorrupted) {
return;
}
$size = @getimagesize($value);
if (empty($size) || (0 === $size[0]) || (0 === $size[1])) {
$this->context->buildViolation($constraint->sizeNotDetectedMessage)
->setCode(Image::SIZE_NOT_DETECTED_ERROR)
->addViolation();
return;
}
$width = $size[0];
$height = $size[1];
if ($constraint->minWidth) {
if (!ctype_digit((string) $constraint->minWidth)) {
throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid minimum width.', $constraint->minWidth));
}
if ($width < $constraint->minWidth) {
$this->context->buildViolation($constraint->minWidthMessage)
->setParameter('{{ width }}', $width)
->setParameter('{{ min_width }}', $constraint->minWidth)
->setCode(Image::TOO_NARROW_ERROR)
->addViolation();
return;
}
}
if ($constraint->maxWidth) {
if (!ctype_digit((string) $constraint->maxWidth)) {
throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid maximum width.', $constraint->maxWidth));
}
if ($width > $constraint->maxWidth) {
$this->context->buildViolation($constraint->maxWidthMessage)
->setParameter('{{ width }}', $width)
->setParameter('{{ max_width }}', $constraint->maxWidth)
->setCode(Image::TOO_WIDE_ERROR)
->addViolation();
return;
}
}
if ($constraint->minHeight) {
if (!ctype_digit((string) $constraint->minHeight)) {
throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid minimum height.', $constraint->minHeight));
}
if ($height < $constraint->minHeight) {
$this->context->buildViolation($constraint->minHeightMessage)
->setParameter('{{ height }}', $height)
->setParameter('{{ min_height }}', $constraint->minHeight)
->setCode(Image::TOO_LOW_ERROR)
->addViolation();
return;
}
}
if ($constraint->maxHeight) {
if (!ctype_digit((string) $constraint->maxHeight)) {
throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid maximum height.', $constraint->maxHeight));
}
if ($height > $constraint->maxHeight) {
$this->context->buildViolation($constraint->maxHeightMessage)
->setParameter('{{ height }}', $height)
->setParameter('{{ max_height }}', $constraint->maxHeight)
->setCode(Image::TOO_HIGH_ERROR)
->addViolation();
}
}
$pixels = $width * $height;
if (null !== $constraint->minPixels) {
if (!ctype_digit((string) $constraint->minPixels)) {
throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid minimum amount of pixels.', $constraint->minPixels));
}
if ($pixels < $constraint->minPixels) {
$this->context->buildViolation($constraint->minPixelsMessage)
->setParameter('{{ pixels }}', $pixels)
->setParameter('{{ min_pixels }}', $constraint->minPixels)
->setParameter('{{ height }}', $height)
->setParameter('{{ width }}', $width)
->setCode(Image::TOO_FEW_PIXEL_ERROR)
->addViolation();
}
}
if (null !== $constraint->maxPixels) {
if (!ctype_digit((string) $constraint->maxPixels)) {
throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid maximum amount of pixels.', $constraint->maxPixels));
}
if ($pixels > $constraint->maxPixels) {
$this->context->buildViolation($constraint->maxPixelsMessage)
->setParameter('{{ pixels }}', $pixels)
->setParameter('{{ max_pixels }}', $constraint->maxPixels)
->setParameter('{{ height }}', $height)
->setParameter('{{ width }}', $width)
->setCode(Image::TOO_MANY_PIXEL_ERROR)
->addViolation();
}
}
$ratio = round($width / $height, 2);
if (null !== $constraint->minRatio) {
if (!is_numeric((string) $constraint->minRatio)) {
throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid minimum ratio.', $constraint->minRatio));
}
if ($ratio < round($constraint->minRatio, 2)) {
$this->context->buildViolation($constraint->minRatioMessage)
->setParameter('{{ ratio }}', $ratio)
->setParameter('{{ min_ratio }}', round($constraint->minRatio, 2))
->setCode(Image::RATIO_TOO_SMALL_ERROR)
->addViolation();
}
}
if (null !== $constraint->maxRatio) {
if (!is_numeric((string) $constraint->maxRatio)) {
throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid maximum ratio.', $constraint->maxRatio));
}
if ($ratio > round($constraint->maxRatio, 2)) {
$this->context->buildViolation($constraint->maxRatioMessage)
->setParameter('{{ ratio }}', $ratio)
->setParameter('{{ max_ratio }}', round($constraint->maxRatio, 2))
->setCode(Image::RATIO_TOO_BIG_ERROR)
->addViolation();
}
}
if (!$constraint->allowSquare && $width == $height) {
$this->context->buildViolation($constraint->allowSquareMessage)
->setParameter('{{ width }}', $width)
->setParameter('{{ height }}', $height)
->setCode(Image::SQUARE_NOT_ALLOWED_ERROR)
->addViolation();
}
if (!$constraint->allowLandscape && $width > $height) {
$this->context->buildViolation($constraint->allowLandscapeMessage)
->setParameter('{{ width }}', $width)
->setParameter('{{ height }}', $height)
->setCode(Image::LANDSCAPE_NOT_ALLOWED_ERROR)
->addViolation();
}
if (!$constraint->allowPortrait && $width < $height) {
$this->context->buildViolation($constraint->allowPortraitMessage)
->setParameter('{{ width }}', $width)
->setParameter('{{ height }}', $height)
->setCode(Image::PORTRAIT_NOT_ALLOWED_ERROR)
->addViolation();
}
if ($constraint->detectCorrupted) {
if (!\function_exists('imagecreatefromstring')) {
throw new LogicException('Corrupted images detection requires installed and enabled GD extension.');
}
$resource = @imagecreatefromstring(file_get_contents($value));
if (false === $resource) {
$this->context->buildViolation($constraint->corruptedMessage)
->setCode(Image::CORRUPTED_IMAGE_ERROR)
->addViolation();
return;
}
}
}
}

View File

@@ -0,0 +1,112 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Exception\InvalidArgumentException;
/**
* Validates that a value is a valid IP address.
*
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Bernhard Schussek <bschussek@gmail.com>
* @author Joseph Bielawski <stloyd@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Ip extends Constraint
{
public const V4 = '4';
public const V6 = '6';
public const ALL = 'all';
// adds FILTER_FLAG_NO_PRIV_RANGE flag (skip private ranges)
public const V4_NO_PRIV = '4_no_priv';
public const V6_NO_PRIV = '6_no_priv';
public const ALL_NO_PRIV = 'all_no_priv';
// adds FILTER_FLAG_NO_RES_RANGE flag (skip reserved ranges)
public const V4_NO_RES = '4_no_res';
public const V6_NO_RES = '6_no_res';
public const ALL_NO_RES = 'all_no_res';
// adds FILTER_FLAG_NO_PRIV_RANGE and FILTER_FLAG_NO_RES_RANGE flags (skip both)
public const V4_ONLY_PUBLIC = '4_public';
public const V6_ONLY_PUBLIC = '6_public';
public const ALL_ONLY_PUBLIC = 'all_public';
public const INVALID_IP_ERROR = 'b1b427ae-9f6f-41b0-aa9b-84511fbb3c5b';
protected const VERSIONS = [
self::V4,
self::V6,
self::ALL,
self::V4_NO_PRIV,
self::V6_NO_PRIV,
self::ALL_NO_PRIV,
self::V4_NO_RES,
self::V6_NO_RES,
self::ALL_NO_RES,
self::V4_ONLY_PUBLIC,
self::V6_ONLY_PUBLIC,
self::ALL_ONLY_PUBLIC,
];
protected const ERROR_NAMES = [
self::INVALID_IP_ERROR => 'INVALID_IP_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const VERSIONS instead
*/
protected static $versions = self::VERSIONS;
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $version = self::V4;
public $message = 'This is not a valid IP address.';
/** @var callable|null */
public $normalizer;
public function __construct(
?array $options = null,
?string $version = null,
?string $message = null,
?callable $normalizer = null,
?array $groups = null,
mixed $payload = null,
) {
parent::__construct($options, $groups, $payload);
$this->version = $version ?? $this->version;
$this->message = $message ?? $this->message;
$this->normalizer = $normalizer ?? $this->normalizer;
if (!\in_array($this->version, self::$versions)) {
throw new ConstraintDefinitionException(\sprintf('The option "version" must be one of "%s".', implode('", "', self::$versions)));
}
if (null !== $this->normalizer && !\is_callable($this->normalizer)) {
throw new InvalidArgumentException(\sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer)));
}
}
}

View File

@@ -0,0 +1,72 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
/**
* Validates whether a value is a valid IP address.
*
* @author Bernhard Schussek <bschussek@gmail.com>
* @author Joseph Bielawski <stloyd@gmail.com>
*/
class IpValidator extends ConstraintValidator
{
/**
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof Ip) {
throw new UnexpectedTypeException($constraint, Ip::class);
}
if (null === $value || '' === $value) {
return;
}
if (!\is_scalar($value) && !$value instanceof \Stringable) {
throw new UnexpectedValueException($value, 'string');
}
$value = (string) $value;
if (null !== $constraint->normalizer) {
$value = ($constraint->normalizer)($value);
}
$flag = match ($constraint->version) {
Ip::V4 => \FILTER_FLAG_IPV4,
Ip::V6 => \FILTER_FLAG_IPV6,
Ip::V4_NO_PRIV => \FILTER_FLAG_IPV4 | \FILTER_FLAG_NO_PRIV_RANGE,
Ip::V6_NO_PRIV => \FILTER_FLAG_IPV6 | \FILTER_FLAG_NO_PRIV_RANGE,
Ip::ALL_NO_PRIV => \FILTER_FLAG_NO_PRIV_RANGE,
Ip::V4_NO_RES => \FILTER_FLAG_IPV4 | \FILTER_FLAG_NO_RES_RANGE,
Ip::V6_NO_RES => \FILTER_FLAG_IPV6 | \FILTER_FLAG_NO_RES_RANGE,
Ip::ALL_NO_RES => \FILTER_FLAG_NO_RES_RANGE,
Ip::V4_ONLY_PUBLIC => \FILTER_FLAG_IPV4 | \FILTER_FLAG_NO_PRIV_RANGE | \FILTER_FLAG_NO_RES_RANGE,
Ip::V6_ONLY_PUBLIC => \FILTER_FLAG_IPV6 | \FILTER_FLAG_NO_PRIV_RANGE | \FILTER_FLAG_NO_RES_RANGE,
Ip::ALL_ONLY_PUBLIC => \FILTER_FLAG_NO_PRIV_RANGE | \FILTER_FLAG_NO_RES_RANGE,
default => 0,
};
if (!filter_var($value, \FILTER_VALIDATE_IP, $flag)) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(Ip::INVALID_IP_ERROR)
->addViolation();
}
}
}

View File

@@ -0,0 +1,44 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class IsFalse extends Constraint
{
public const NOT_FALSE_ERROR = 'd53a91b0-def3-426a-83d7-269da7ab4200';
protected const ERROR_NAMES = [
self::NOT_FALSE_ERROR => 'NOT_FALSE_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $message = 'This value should be false.';
public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null)
{
parent::__construct($options ?? [], $groups, $payload);
$this->message = $message ?? $this->message;
}
}

View File

@@ -0,0 +1,41 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class IsFalseValidator extends ConstraintValidator
{
/**
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof IsFalse) {
throw new UnexpectedTypeException($constraint, IsFalse::class);
}
if (null === $value || false === $value || 0 === $value || '0' === $value) {
return;
}
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(IsFalse::NOT_FALSE_ERROR)
->addViolation();
}
}

View File

@@ -0,0 +1,44 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class IsNull extends Constraint
{
public const NOT_NULL_ERROR = '60d2f30b-8cfa-4372-b155-9656634de120';
protected const ERROR_NAMES = [
self::NOT_NULL_ERROR => 'NOT_NULL_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $message = 'This value should be null.';
public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null)
{
parent::__construct($options ?? [], $groups, $payload);
$this->message = $message ?? $this->message;
}
}

View File

@@ -0,0 +1,39 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class IsNullValidator extends ConstraintValidator
{
/**
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof IsNull) {
throw new UnexpectedTypeException($constraint, IsNull::class);
}
if (null !== $value) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(IsNull::NOT_NULL_ERROR)
->addViolation();
}
}
}

View File

@@ -0,0 +1,44 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class IsTrue extends Constraint
{
public const NOT_TRUE_ERROR = '2beabf1c-54c0-4882-a928-05249b26e23b';
protected const ERROR_NAMES = [
self::NOT_TRUE_ERROR => 'NOT_TRUE_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $message = 'This value should be true.';
public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null)
{
parent::__construct($options ?? [], $groups, $payload);
$this->message = $message ?? $this->message;
}
}

View File

@@ -0,0 +1,41 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class IsTrueValidator extends ConstraintValidator
{
/**
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof IsTrue) {
throw new UnexpectedTypeException($constraint, IsTrue::class);
}
if (null === $value || true === $value || 1 === $value || '1' === $value) {
return;
}
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(IsTrue::NOT_TRUE_ERROR)
->addViolation();
}
}

View File

@@ -0,0 +1,83 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author The Whole Life To Learn <thewholelifetolearn@gmail.com>
* @author Manuel Reinhard <manu@sprain.ch>
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Isbn extends Constraint
{
public const ISBN_10 = 'isbn10';
public const ISBN_13 = 'isbn13';
public const TOO_SHORT_ERROR = '949acbb0-8ef5-43ed-a0e9-032dfd08ae45';
public const TOO_LONG_ERROR = '3171387d-f80a-47b3-bd6e-60598545316a';
public const INVALID_CHARACTERS_ERROR = '23d21cea-da99-453d-98b1-a7d916fbb339';
public const CHECKSUM_FAILED_ERROR = '2881c032-660f-46b6-8153-d352d9706640';
public const TYPE_NOT_RECOGNIZED_ERROR = 'fa54a457-f042-441f-89c4-066ee5bdd3e1';
protected const ERROR_NAMES = [
self::TOO_SHORT_ERROR => 'TOO_SHORT_ERROR',
self::TOO_LONG_ERROR => 'TOO_LONG_ERROR',
self::INVALID_CHARACTERS_ERROR => 'INVALID_CHARACTERS_ERROR',
self::CHECKSUM_FAILED_ERROR => 'CHECKSUM_FAILED_ERROR',
self::TYPE_NOT_RECOGNIZED_ERROR => 'TYPE_NOT_RECOGNIZED_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $isbn10Message = 'This value is not a valid ISBN-10.';
public $isbn13Message = 'This value is not a valid ISBN-13.';
public $bothIsbnMessage = 'This value is neither a valid ISBN-10 nor a valid ISBN-13.';
public $type;
public $message;
public function __construct(
string|array|null $type = null,
?string $message = null,
?string $isbn10Message = null,
?string $isbn13Message = null,
?string $bothIsbnMessage = null,
?array $groups = null,
mixed $payload = null,
array $options = [],
) {
if (\is_array($type)) {
$options = array_merge($type, $options);
} elseif (null !== $type) {
$options['value'] = $type;
}
parent::__construct($options, $groups, $payload);
$this->message = $message ?? $this->message;
$this->isbn10Message = $isbn10Message ?? $this->isbn10Message;
$this->isbn13Message = $isbn13Message ?? $this->isbn13Message;
$this->bothIsbnMessage = $bothIsbnMessage ?? $this->bothIsbnMessage;
}
public function getDefaultOption(): ?string
{
return 'type';
}
}

View File

@@ -0,0 +1,193 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
/**
* Validates whether the value is a valid ISBN-10 or ISBN-13.
*
* @author The Whole Life To Learn <thewholelifetolearn@gmail.com>
* @author Manuel Reinhard <manu@sprain.ch>
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @see https://en.wikipedia.org/wiki/Isbn
*/
class IsbnValidator extends ConstraintValidator
{
/**
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof Isbn) {
throw new UnexpectedTypeException($constraint, Isbn::class);
}
if (null === $value || '' === $value) {
return;
}
if (!\is_scalar($value) && !$value instanceof \Stringable) {
throw new UnexpectedValueException($value, 'string');
}
$value = (string) $value;
$canonical = str_replace('-', '', $value);
// Explicitly validate against ISBN-10
if (Isbn::ISBN_10 === $constraint->type) {
if (true !== ($code = $this->validateIsbn10($canonical))) {
$this->context->buildViolation($this->getMessage($constraint, $constraint->type))
->setParameter('{{ value }}', $this->formatValue($value))
->setCode($code)
->addViolation();
}
return;
}
// Explicitly validate against ISBN-13
if (Isbn::ISBN_13 === $constraint->type) {
if (true !== ($code = $this->validateIsbn13($canonical))) {
$this->context->buildViolation($this->getMessage($constraint, $constraint->type))
->setParameter('{{ value }}', $this->formatValue($value))
->setCode($code)
->addViolation();
}
return;
}
// Try both ISBNs
// First, try ISBN-10
$code = $this->validateIsbn10($canonical);
// The ISBN can only be an ISBN-13 if the value was too long for ISBN-10
if (Isbn::TOO_LONG_ERROR === $code) {
// Try ISBN-13 now
$code = $this->validateIsbn13($canonical);
// If too short, this means we have 11 or 12 digits
if (Isbn::TOO_SHORT_ERROR === $code) {
$code = Isbn::TYPE_NOT_RECOGNIZED_ERROR;
}
}
if (true !== $code) {
$this->context->buildViolation($this->getMessage($constraint))
->setParameter('{{ value }}', $this->formatValue($value))
->setCode($code)
->addViolation();
}
}
/**
* @return string|bool
*/
protected function validateIsbn10(string $isbn)
{
// Choose an algorithm so that ERROR_INVALID_CHARACTERS is preferred
// over ERROR_TOO_SHORT/ERROR_TOO_LONG
// Otherwise "0-45122-5244" passes, but "0-45122_5244" reports
// "too long"
// Error priority:
// 1. ERROR_INVALID_CHARACTERS
// 2. ERROR_TOO_SHORT/ERROR_TOO_LONG
// 3. ERROR_CHECKSUM_FAILED
$checkSum = 0;
for ($i = 0; $i < 10; ++$i) {
// If we test the length before the loop, we get an ERROR_TOO_SHORT
// when actually an ERROR_INVALID_CHARACTERS is wanted, e.g. for
// "0-45122_5244" (typo)
if (!isset($isbn[$i])) {
return Isbn::TOO_SHORT_ERROR;
}
if ('X' === $isbn[$i]) {
$digit = 10;
} elseif (ctype_digit($isbn[$i])) {
$digit = $isbn[$i];
} else {
return Isbn::INVALID_CHARACTERS_ERROR;
}
$checkSum += $digit * (10 - $i);
}
if (isset($isbn[$i])) {
return Isbn::TOO_LONG_ERROR;
}
return 0 === $checkSum % 11 ? true : Isbn::CHECKSUM_FAILED_ERROR;
}
/**
* @return string|bool
*/
protected function validateIsbn13(string $isbn)
{
// Error priority:
// 1. ERROR_INVALID_CHARACTERS
// 2. ERROR_TOO_SHORT/ERROR_TOO_LONG
// 3. ERROR_CHECKSUM_FAILED
if (!ctype_digit($isbn)) {
return Isbn::INVALID_CHARACTERS_ERROR;
}
$length = \strlen($isbn);
if ($length < 13) {
return Isbn::TOO_SHORT_ERROR;
}
if ($length > 13) {
return Isbn::TOO_LONG_ERROR;
}
$checkSum = 0;
for ($i = 0; $i < 13; $i += 2) {
$checkSum += $isbn[$i];
}
for ($i = 1; $i < 12; $i += 2) {
$checkSum += $isbn[$i] * 3;
}
return 0 === $checkSum % 10 ? true : Isbn::CHECKSUM_FAILED_ERROR;
}
/**
* @return string
*/
protected function getMessage(Isbn $constraint, ?string $type = null)
{
if (null !== $constraint->message) {
return $constraint->message;
} elseif (Isbn::ISBN_10 === $type) {
return $constraint->isbn10Message;
} elseif (Isbn::ISBN_13 === $type) {
return $constraint->isbn13Message;
}
return $constraint->bothIsbnMessage;
}
}

View File

@@ -0,0 +1,51 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @author Laurent Masforné <l.masforne@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Isin extends Constraint
{
public const VALIDATION_LENGTH = 12;
public const VALIDATION_PATTERN = '/[A-Z]{2}[A-Z0-9]{9}[0-9]{1}/';
public const INVALID_LENGTH_ERROR = '88738dfc-9ed5-ba1e-aebe-402a2a9bf58e';
public const INVALID_PATTERN_ERROR = '3d08ce0-ded9-a93d-9216-17ac21265b65e';
public const INVALID_CHECKSUM_ERROR = '32089b-0ee1-93ba-399e-aa232e62f2d29d';
protected const ERROR_NAMES = [
self::INVALID_LENGTH_ERROR => 'INVALID_LENGTH_ERROR',
self::INVALID_PATTERN_ERROR => 'INVALID_PATTERN_ERROR',
self::INVALID_CHECKSUM_ERROR => 'INVALID_CHECKSUM_ERROR',
];
/**
* @deprecated since Symfony 6.1, use const ERROR_NAMES instead
*/
protected static $errorNames = self::ERROR_NAMES;
public $message = 'This value is not a valid International Securities Identification Number (ISIN).';
public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null)
{
parent::__construct($options, $groups, $payload);
$this->message = $message ?? $this->message;
}
}

View File

@@ -0,0 +1,81 @@
<?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\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
/**
* @author Laurent Masforné <l.masforne@gmail.com>
*
* @see https://en.wikipedia.org/wiki/International_Securities_Identification_Number
*/
class IsinValidator extends ConstraintValidator
{
/**
* @return void
*/
public function validate(mixed $value, Constraint $constraint)
{
if (!$constraint instanceof Isin) {
throw new UnexpectedTypeException($constraint, Isin::class);
}
if (null === $value || '' === $value) {
return;
}
if (!\is_scalar($value) && !$value instanceof \Stringable) {
throw new UnexpectedValueException($value, 'string');
}
$value = strtoupper($value);
if (Isin::VALIDATION_LENGTH !== \strlen($value)) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(Isin::INVALID_LENGTH_ERROR)
->addViolation();
return;
}
if (!preg_match(Isin::VALIDATION_PATTERN, $value)) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(Isin::INVALID_PATTERN_ERROR)
->addViolation();
return;
}
if (!$this->isCorrectChecksum($value)) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(Isin::INVALID_CHECKSUM_ERROR)
->addViolation();
}
}
private function isCorrectChecksum(string $input): bool
{
$characters = str_split($input);
foreach ($characters as $i => $char) {
$characters[$i] = \intval($char, 36);
}
$number = implode('', $characters);
return 0 === $this->context->getValidator()->validate($number, new Luhn())->count();
}
}

Some files were not shown because too many files have changed in this diff Show More