From 4f5b44bd3bd490309eb2ba7b44df4769816ba729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Sat, 3 Aug 2013 19:26:54 +0200 Subject: twig implementation --- .../Symfony/Component/EventDispatcher/.gitignore | 4 + .../Symfony/Component/EventDispatcher/CHANGELOG.md | 16 + .../ContainerAwareEventDispatcher.php | 202 ++ .../Debug/TraceableEventDispatcherInterface.php | 32 + .../Symfony/Component/EventDispatcher/Event.php | 121 ++ .../Component/EventDispatcher/EventDispatcher.php | 185 ++ .../EventDispatcher/EventDispatcherInterface.php | 96 + .../EventDispatcher/EventSubscriberInterface.php | 50 + .../Component/EventDispatcher/GenericEvent.php | 186 ++ .../EventDispatcher/ImmutableEventDispatcher.php | 92 + .../Symfony/Component/EventDispatcher/LICENSE | 19 + .../Symfony/Component/EventDispatcher/README.md | 25 + .../Tests/ContainerAwareEventDispatcherTest.php | 257 +++ .../EventDispatcher/Tests/EventDispatcherTest.php | 320 +++ .../Component/EventDispatcher/Tests/EventTest.php | 84 + .../EventDispatcher/Tests/GenericEventTest.php | 140 ++ .../Tests/ImmutableEventDispatcherTest.php | 106 + .../Component/EventDispatcher/composer.json | 38 + .../Component/EventDispatcher/phpunit.xml.dist | 30 + .../Symfony/Component/Filesystem/.gitignore | 4 + .../Symfony/Component/Filesystem/CHANGELOG.md | 18 + .../Filesystem/Exception/ExceptionInterface.php | 24 + .../Component/Filesystem/Exception/IOException.php | 24 + .../Symfony/Component/Filesystem/Filesystem.php | 471 +++++ .../Symfony/Component/Filesystem/LICENSE | 19 + .../Symfony/Component/Filesystem/README.md | 45 + .../Component/Filesystem/Tests/FilesystemTest.php | 982 +++++++++ .../Symfony/Component/Filesystem/composer.json | 31 + .../Symfony/Component/Filesystem/phpunit.xml.dist | 28 + .../symfony/form/Symfony/Component/Form/.gitignore | 4 + .../Symfony/Component/Form/AbstractExtension.php | 195 ++ .../Component/Form/AbstractRendererEngine.php | 206 ++ .../form/Symfony/Component/Form/AbstractType.php | 56 + .../Component/Form/AbstractTypeExtension.php | 48 + .../symfony/form/Symfony/Component/Form/Button.php | 436 ++++ .../form/Symfony/Component/Form/ButtonBuilder.php | 864 ++++++++ .../Symfony/Component/Form/ButtonTypeInterface.php | 21 + .../form/Symfony/Component/Form/CHANGELOG.md | 238 +++ .../Symfony/Component/Form/CallbackTransformer.php | 70 + .../Symfony/Component/Form/ClickableInterface.php | 27 + .../Symfony/Component/Form/DataMapperInterface.php | 38 + .../Component/Form/DataTransformerInterface.php | 77 + .../Form/Exception/AlreadyBoundException.php | 22 + .../Form/Exception/AlreadySubmittedException.php | 22 + .../Form/Exception/BadMethodCallException.php | 21 + .../Form/Exception/ErrorMappingException.php | 16 + .../Form/Exception/ExceptionInterface.php | 21 + .../Form/Exception/InvalidArgumentException.php | 21 + .../Exception/InvalidConfigurationException.php | 16 + .../Component/Form/Exception/LogicException.php | 21 + .../Form/Exception/OutOfBoundsException.php | 21 + .../Component/Form/Exception/RuntimeException.php | 21 + .../Form/Exception/StringCastException.php | 16 + .../Exception/TransformationFailedException.php | 21 + .../Form/Exception/UnexpectedTypeException.php | 20 + .../Form/Extension/Core/ChoiceList/ChoiceList.php | 510 +++++ .../Core/ChoiceList/ChoiceListInterface.php | 149 ++ .../Extension/Core/ChoiceList/LazyChoiceList.php | 149 ++ .../Extension/Core/ChoiceList/ObjectChoiceList.php | 184 ++ .../Extension/Core/ChoiceList/SimpleChoiceList.php | 164 ++ .../Form/Extension/Core/CoreExtension.php | 59 + .../Core/DataMapper/PropertyPathMapper.php | 92 + .../DataTransformer/ArrayToPartsTransformer.php | 86 + .../DataTransformer/BaseDateTimeTransformer.php | 52 + .../DataTransformer/BooleanToStringTransformer.php | 85 + .../ChoiceToBooleanArrayTransformer.php | 118 ++ .../DataTransformer/ChoiceToValueTransformer.php | 62 + .../ChoicesToBooleanArrayTransformer.php | 117 ++ .../DataTransformer/ChoicesToValuesTransformer.php | 83 + .../Core/DataTransformer/DataTransformerChain.php | 86 + .../DataTransformer/DateTimeToArrayTransformer.php | 184 ++ .../DateTimeToLocalizedStringTransformer.php | 169 ++ .../DateTimeToRfc3339Transformer.php | 82 + .../DateTimeToStringTransformer.php | 231 ++ .../DateTimeToTimestampTransformer.php | 89 + .../IntegerToLocalizedStringTransformer.php | 53 + .../MoneyToLocalizedStringTransformer.php | 90 + .../NumberToLocalizedStringTransformer.php | 184 ++ .../PercentToLocalizedStringTransformer.php | 149 ++ .../ValueToDuplicatesTransformer.php | 91 + .../EventListener/FixCheckboxInputListener.php | 62 + .../Core/EventListener/FixRadioInputListener.php | 66 + .../Core/EventListener/FixUrlProtocolListener.php | 56 + .../Core/EventListener/MergeCollectionListener.php | 137 ++ .../Core/EventListener/ResizeFormListener.php | 173 ++ .../Extension/Core/EventListener/TrimListener.php | 55 + .../Form/Extension/Core/Type/BaseType.php | 121 ++ .../Form/Extension/Core/Type/BirthdayType.php | 44 + .../Form/Extension/Core/Type/ButtonType.php | 38 + .../Form/Extension/Core/Type/CheckboxType.php | 67 + .../Form/Extension/Core/Type/ChoiceType.php | 274 +++ .../Form/Extension/Core/Type/CollectionType.php | 103 + .../Form/Extension/Core/Type/CountryType.php | 45 + .../Form/Extension/Core/Type/CurrencyType.php | 45 + .../Form/Extension/Core/Type/DateTimeType.php | 281 +++ .../Form/Extension/Core/Type/DateType.php | 309 +++ .../Form/Extension/Core/Type/EmailType.php | 33 + .../Form/Extension/Core/Type/FileType.php | 61 + .../Form/Extension/Core/Type/FormType.php | 214 ++ .../Form/Extension/Core/Type/HiddenType.php | 40 + .../Form/Extension/Core/Type/IntegerType.php | 68 + .../Form/Extension/Core/Type/LanguageType.php | 45 + .../Form/Extension/Core/Type/LocaleType.php | 46 + .../Form/Extension/Core/Type/MoneyType.php | 111 + .../Form/Extension/Core/Type/NumberType.php | 66 + .../Form/Extension/Core/Type/PasswordType.php | 57 + .../Form/Extension/Core/Type/PercentType.php | 55 + .../Form/Extension/Core/Type/RadioType.php | 33 + .../Form/Extension/Core/Type/RepeatedType.php | 67 + .../Form/Extension/Core/Type/ResetType.php | 39 + .../Form/Extension/Core/Type/SearchType.php | 33 + .../Form/Extension/Core/Type/SubmitType.php | 46 + .../Form/Extension/Core/Type/TextType.php | 36 + .../Form/Extension/Core/Type/TextareaType.php | 43 + .../Form/Extension/Core/Type/TimeType.php | 225 ++ .../Form/Extension/Core/Type/TimezoneType.php | 86 + .../Component/Form/Extension/Core/Type/UrlType.php | 54 + .../Form/Extension/Core/View/ChoiceView.php | 55 + .../Form/Extension/Csrf/CsrfExtension.php | 64 + .../Csrf/CsrfProvider/CsrfProviderInterface.php | 49 + .../Csrf/CsrfProvider/DefaultCsrfProvider.php | 78 + .../Csrf/CsrfProvider/SessionCsrfProvider.php | 57 + .../Csrf/EventListener/CsrfValidationListener.php | 115 + .../Extension/Csrf/Type/FormTypeCsrfExtension.php | 129 ++ .../DependencyInjectionExtension.php | 101 + .../EventListener/BindRequestListener.php | 91 + .../HttpFoundation/HttpFoundationExtension.php | 29 + .../HttpFoundationRequestHandler.php | 79 + .../Type/FormTypeHttpFoundationExtension.php | 56 + .../Extension/Templating/TemplatingExtension.php | 33 + .../Templating/TemplatingRendererEngine.php | 125 ++ .../Form/Extension/Validator/Constraints/Form.php | 33 + .../Validator/Constraints/FormValidator.php | 236 +++ .../Validator/EventListener/ValidationListener.php | 68 + .../Validator/Type/BaseValidatorExtension.php | 56 + .../Validator/Type/FormTypeValidatorExtension.php | 84 + .../Type/RepeatedTypeValidatorExtension.php | 45 + .../Type/SubmitTypeValidatorExtension.php | 28 + .../Form/Extension/Validator/Util/ServerParams.php | 64 + .../Extension/Validator/ValidatorExtension.php | 57 + .../Extension/Validator/ValidatorTypeGuesser.php | 286 +++ .../Validator/ViolationMapper/MappingRule.php | 106 + .../Validator/ViolationMapper/RelativePath.php | 45 + .../Validator/ViolationMapper/ViolationMapper.php | 299 +++ .../ViolationMapper/ViolationMapperInterface.php | 33 + .../Validator/ViolationMapper/ViolationPath.php | 250 +++ .../ViolationMapper/ViolationPathIterator.php | 30 + .../symfony/form/Symfony/Component/Form/Form.php | 1046 +++++++++ .../form/Symfony/Component/Form/FormBuilder.php | 275 +++ .../Component/Form/FormBuilderInterface.php | 87 + .../Symfony/Component/Form/FormConfigBuilder.php | 919 ++++++++ .../Component/Form/FormConfigBuilderInterface.php | 287 +++ .../Symfony/Component/Form/FormConfigInterface.php | 243 +++ .../form/Symfony/Component/Form/FormError.php | 105 + .../form/Symfony/Component/Form/FormEvent.php | 65 + .../form/Symfony/Component/Form/FormEvents.php | 49 + .../Component/Form/FormExtensionInterface.php | 63 + .../form/Symfony/Component/Form/FormFactory.php | 156 ++ .../Symfony/Component/Form/FormFactoryBuilder.php | 162 ++ .../Component/Form/FormFactoryBuilderInterface.php | 108 + .../Component/Form/FormFactoryInterface.php | 109 + .../form/Symfony/Component/Form/FormInterface.php | 288 +++ .../form/Symfony/Component/Form/FormRegistry.php | 180 ++ .../Component/Form/FormRegistryInterface.php | 57 + .../form/Symfony/Component/Form/FormRenderer.php | 304 +++ .../Component/Form/FormRendererEngineInterface.php | 150 ++ .../Component/Form/FormRendererInterface.php | 103 + .../Component/Form/FormTypeExtensionInterface.php | 75 + .../Component/Form/FormTypeGuesserChain.php | 104 + .../Component/Form/FormTypeGuesserInterface.php | 64 + .../Symfony/Component/Form/FormTypeInterface.php | 95 + .../form/Symfony/Component/Form/FormView.php | 159 ++ .../symfony/form/Symfony/Component/Form/Forms.php | 185 ++ .../form/Symfony/Component/Form/Guess/Guess.php | 113 + .../Symfony/Component/Form/Guess/TypeGuess.php | 70 + .../Symfony/Component/Form/Guess/ValueGuess.php | 50 + vendor/symfony/form/Symfony/Component/Form/LICENSE | 19 + .../Component/Form/NativeRequestHandler.php | 194 ++ .../Symfony/Component/Form/PreloadedExtension.php | 97 + .../symfony/form/Symfony/Component/Form/README.md | 26 + .../Component/Form/RequestHandlerInterface.php | 28 + .../Symfony/Component/Form/ResolvedFormType.php | 284 +++ .../Component/Form/ResolvedFormTypeFactory.php | 26 + .../Form/ResolvedFormTypeFactoryInterface.php | 38 + .../Component/Form/ResolvedFormTypeInterface.php | 106 + .../Component/Form/Resources/config/validation.xml | 13 + .../Form/Resources/translations/validators.ar.xlf | 19 + .../Form/Resources/translations/validators.bg.xlf | 19 + .../Form/Resources/translations/validators.ca.xlf | 19 + .../Form/Resources/translations/validators.cs.xlf | 19 + .../Form/Resources/translations/validators.da.xlf | 19 + .../Form/Resources/translations/validators.de.xlf | 19 + .../Form/Resources/translations/validators.el.xlf | 19 + .../Form/Resources/translations/validators.en.xlf | 19 + .../Form/Resources/translations/validators.es.xlf | 19 + .../Form/Resources/translations/validators.et.xlf | 19 + .../Form/Resources/translations/validators.eu.xlf | 19 + .../Form/Resources/translations/validators.fa.xlf | 19 + .../Form/Resources/translations/validators.fi.xlf | 19 + .../Form/Resources/translations/validators.fr.xlf | 19 + .../Form/Resources/translations/validators.gl.xlf | 19 + .../Form/Resources/translations/validators.he.xlf | 19 + .../Form/Resources/translations/validators.hr.xlf | 19 + .../Form/Resources/translations/validators.hu.xlf | 19 + .../Form/Resources/translations/validators.hy.xlf | 19 + .../Form/Resources/translations/validators.id.xlf | 19 + .../Form/Resources/translations/validators.it.xlf | 19 + .../Form/Resources/translations/validators.ja.xlf | 19 + .../Form/Resources/translations/validators.lb.xlf | 19 + .../Form/Resources/translations/validators.lt.xlf | 19 + .../Form/Resources/translations/validators.lv.xlf | 19 + .../Form/Resources/translations/validators.mn.xlf | 19 + .../Form/Resources/translations/validators.nb.xlf | 19 + .../Form/Resources/translations/validators.nl.xlf | 19 + .../Form/Resources/translations/validators.pl.xlf | 19 + .../Form/Resources/translations/validators.pt.xlf | 19 + .../Resources/translations/validators.pt_BR.xlf | 19 + .../Form/Resources/translations/validators.ro.xlf | 19 + .../Form/Resources/translations/validators.ru.xlf | 19 + .../Form/Resources/translations/validators.sk.xlf | 19 + .../Form/Resources/translations/validators.sl.xlf | 19 + .../Resources/translations/validators.sr_Cyrl.xlf | 19 + .../Resources/translations/validators.sr_Latn.xlf | 19 + .../Form/Resources/translations/validators.sv.xlf | 19 + .../Form/Resources/translations/validators.ua.xlf | 19 + .../Resources/translations/validators.zh_CN.xlf | 19 + .../Symfony/Component/Form/ReversedTransformer.php | 55 + .../form/Symfony/Component/Form/SubmitButton.php | 52 + .../Symfony/Component/Form/SubmitButtonBuilder.php | 30 + .../Component/Form/SubmitButtonTypeInterface.php | 21 + .../Form/Test/DeprecationErrorHandler.php | 42 + .../Component/Form/Test/FormBuilderInterface.php | 16 + .../Form/Test/FormIntegrationTestCase.php | 41 + .../Symfony/Component/Form/Test/FormInterface.php | 16 + .../Form/Test/FormPerformanceTestCase.php | 70 + .../Symfony/Component/Form/Test/TypeTestCase.php | 41 + .../Component/Form/Tests/AbstractDivLayoutTest.php | 732 +++++++ .../Component/Form/Tests/AbstractExtensionTest.php | 43 + .../Component/Form/Tests/AbstractFormTest.php | 147 ++ .../Component/Form/Tests/AbstractLayoutTest.php | 1876 +++++++++++++++++ .../Form/Tests/AbstractRequestHandlerTest.php | 280 +++ .../Form/Tests/AbstractTableLayoutTest.php | 509 +++++ .../Form/Tests/CompoundFormPerformanceTest.php | 48 + .../Component/Form/Tests/CompoundFormTest.php | 759 +++++++ .../Extension/Core/ChoiceList/ChoiceListTest.php | 200 ++ .../Core/ChoiceList/LazyChoiceListTest.php | 116 + .../Core/ChoiceList/ObjectChoiceListTest.php | 212 ++ .../Core/ChoiceList/SimpleChoiceListTest.php | 188 ++ .../Core/DataMapper/PropertyPathMapperTest.php | 319 +++ .../ArrayToPartsTransformerTest.php | 149 ++ .../BooleanToStringTransformerTest.php | 60 + .../ChoiceToValueTransformerTest.php | 76 + .../ChoicesToValuesTransformerTest.php | 76 + .../DataTransformer/DataTransformerChainTest.php | 53 + .../Core/DataTransformer/DateTimeTestCase.php | 20 + .../DateTimeToArrayTransformerTest.php | 512 +++++ .../DateTimeToLocalizedStringTransformerTest.php | 275 +++ .../DateTimeToRfc3339TransformerTest.php | 132 ++ .../DateTimeToStringTransformerTest.php | 181 ++ .../DateTimeToTimestampTransformerTest.php | 104 + .../IntegerToLocalizedStringTransformerTest.php | 115 + .../MoneyToLocalizedStringTransformerTest.php | 74 + .../NumberToLocalizedStringTransformerTest.php | 393 ++++ .../PercentToLocalizedStringTransformerTest.php | 114 + .../ValueToDuplicatesTransformerTest.php | 120 ++ .../EventListener/FixRadioInputListenerTest.php | 106 + .../EventListener/FixUrlProtocolListenerTest.php | 61 + .../Core/EventListener/Fixtures/randomhash | Bin 0 -> 35 bytes .../MergeCollectionListenerArrayObjectTest.php | 27 + .../MergeCollectionListenerArrayTest.php | 27 + ...ergeCollectionListenerCustomArrayObjectTest.php | 28 + .../EventListener/MergeCollectionListenerTest.php | 259 +++ .../Core/EventListener/ResizeFormListenerTest.php | 255 +++ .../Core/EventListener/TrimListenerTest.php | 79 + .../Tests/Extension/Core/Type/BaseTypeTest.php | 135 ++ .../Tests/Extension/Core/Type/ButtonTypeTest.php | 28 + .../Tests/Extension/Core/Type/CheckboxTypeTest.php | 162 ++ .../Core/Type/ChoiceTypePerformanceTest.php | 38 + .../Tests/Extension/Core/Type/ChoiceTypeTest.php | 949 +++++++++ .../Extension/Core/Type/CollectionTypeTest.php | 200 ++ .../Tests/Extension/Core/Type/CountryTypeTest.php | 52 + .../Tests/Extension/Core/Type/CurrencyTypeTest.php | 37 + .../Tests/Extension/Core/Type/DateTimeTypeTest.php | 477 +++++ .../Tests/Extension/Core/Type/DateTypeTest.php | 781 +++++++ .../Tests/Extension/Core/Type/FileTypeTest.php | 83 + .../Tests/Extension/Core/Type/FormTypeTest.php | 578 +++++ .../Tests/Extension/Core/Type/IntegerTypeTest.php | 34 + .../Tests/Extension/Core/Type/LanguageTypeTest.php | 47 + .../Tests/Extension/Core/Type/LocaleTypeTest.php | 36 + .../Tests/Extension/Core/Type/MoneyTypeTest.php | 59 + .../Tests/Extension/Core/Type/NumberTypeTest.php | 63 + .../Tests/Extension/Core/Type/PasswordTypeTest.php | 51 + .../Tests/Extension/Core/Type/RepeatedTypeTest.php | 149 ++ .../Tests/Extension/Core/Type/SubmitTypeTest.php | 54 + .../Tests/Extension/Core/Type/TimeTypeTest.php | 649 ++++++ .../Tests/Extension/Core/Type/TimezoneTypeTest.php | 30 + .../Tests/Extension/Core/Type/TypeTestCase.php | 21 + .../Form/Tests/Extension/Core/Type/UrlTypeTest.php | 61 + .../Csrf/CsrfProvider/DefaultCsrfProviderTest.php | 81 + .../Csrf/CsrfProvider/SessionCsrfProviderTest.php | 75 + .../EventListener/CsrfValidationListenerTest.php | 78 + .../Csrf/Type/FormTypeCsrfExtensionTest.php | 301 +++ .../EventListener/BindRequestListenerTest.php | 286 +++ .../HttpFoundationRequestHandlerTest.php | 54 + .../Validator/Constraints/FormValidatorTest.php | 748 +++++++ .../EventListener/ValidationListenerTest.php | 145 ++ .../Type/FormTypeValidatorExtensionTest.php | 85 + .../Extension/Validator/Type/TypeTestCase.php | 49 + .../Extension/Validator/Util/ServerParamsTest.php | 46 + .../ViolationMapper/ViolationMapperTest.php | 1481 +++++++++++++ .../ViolationMapper/ViolationPathTest.php | 245 +++ .../Form/Tests/Fixtures/AlternatingRowType.php | 27 + .../Component/Form/Tests/Fixtures/Author.php | 71 + .../Component/Form/Tests/Fixtures/AuthorType.php | 30 + .../Form/Tests/Fixtures/CustomArrayObject.php | 70 + .../Form/Tests/Fixtures/FixedDataTransformer.php | 45 + .../Form/Tests/Fixtures/FixedFilterListener.php | 66 + .../Component/Form/Tests/Fixtures/FooSubType.php | 32 + .../Fixtures/FooSubTypeWithParentInstance.php | 32 + .../Component/Form/Tests/Fixtures/FooType.php | 32 + .../Form/Tests/Fixtures/FooTypeBarExtension.php | 35 + .../Form/Tests/Fixtures/FooTypeBazExtension.php | 28 + .../Form/Tests/Fixtures/TestExtension.php | 72 + .../form/Symfony/Component/Form/Tests/Fixtures/foo | 0 .../Component/Form/Tests/FormBuilderTest.php | 232 ++ .../Component/Form/Tests/FormConfigTest.php | 148 ++ .../Form/Tests/FormFactoryBuilderTest.php | 59 + .../Component/Form/Tests/FormFactoryTest.php | 506 +++++ .../Form/Tests/FormIntegrationTestCase.php | 21 + .../Form/Tests/FormPerformanceTestCase.php | 21 + .../Component/Form/Tests/FormRegistryTest.php | 243 +++ .../Component/Form/Tests/FormRendererTest.php | 27 + .../Component/Form/Tests/Guess/GuessTest.php | 36 + .../Form/Tests/NativeRequestHandlerTest.php | 219 ++ .../Component/Form/Tests/ResolvedFormTypeTest.php | 280 +++ .../Component/Form/Tests/SimpleFormTest.php | 1045 +++++++++ .../form/Symfony/Component/Form/Util/FormUtil.php | 42 + .../Form/Util/InheritDataAwareIterator.php | 35 + .../Form/Util/VirtualFormAwareIterator.php | 50 + .../form/Symfony/Component/Form/composer.json | 43 + .../form/Symfony/Component/Form/phpunit.xml.dist | 29 + .../symfony/icu/Symfony/Component/Icu/.gitignore | 3 + .../Symfony/Component/Icu/IcuCurrencyBundle.php | 28 + .../symfony/icu/Symfony/Component/Icu/IcuData.php | 66 + .../Symfony/Component/Icu/IcuLanguageBundle.php | 28 + .../icu/Symfony/Component/Icu/IcuLocaleBundle.php | 28 + .../icu/Symfony/Component/Icu/IcuRegionBundle.php | 28 + vendor/symfony/icu/Symfony/Component/Icu/LICENSE | 19 + vendor/symfony/icu/Symfony/Component/Icu/README.md | 18 + .../Component/Icu/Resources/data/curr/en.php | 1791 ++++++++++++++++ .../Component/Icu/Resources/data/lang/en.php | 750 +++++++ .../Component/Icu/Resources/data/locales/en.php | 305 +++ .../Component/Icu/Resources/data/region/en.php | 273 +++ .../Component/Icu/Resources/data/version.txt | 1 + .../Component/Icu/Tests/IcuIntegrationTest.php | 55 + .../icu/Symfony/Component/Icu/composer.json | 26 + .../icu/Symfony/Component/Icu/phpunit.xml.dist | 29 + .../symfony/intl/Symfony/Component/Intl/.gitignore | 3 + .../intl/Symfony/Component/Intl/CONTRIBUTING.md | 91 + .../Symfony/Component/Intl/Collator/Collator.php | 295 +++ .../DateFormatter/DateFormat/AmPmTransformer.php | 46 + .../DateFormat/DayOfWeekTransformer.php | 59 + .../DateFormat/DayOfYearTransformer.php | 46 + .../DateFormatter/DateFormat/DayTransformer.php | 46 + .../DateFormatter/DateFormat/FullTransformer.php | 356 ++++ .../DateFormat/Hour1200Transformer.php | 62 + .../DateFormat/Hour1201Transformer.php | 62 + .../DateFormat/Hour2400Transformer.php | 61 + .../DateFormat/Hour2401Transformer.php | 64 + .../DateFormatter/DateFormat/HourTransformer.php | 30 + .../DateFormatter/DateFormat/MinuteTransformer.php | 48 + .../DateFormatter/DateFormat/MonthTransformer.php | 143 ++ .../DateFormat/QuarterTransformer.php | 64 + .../DateFormatter/DateFormat/SecondTransformer.php | 48 + .../DateFormat/TimeZoneTransformer.php | 99 + .../Intl/DateFormatter/DateFormat/Transformer.php | 64 + .../DateFormatter/DateFormat/YearTransformer.php | 50 + .../Intl/DateFormatter/IntlDateFormatter.php | 631 ++++++ .../Intl/Exception/BadMethodCallException.php | 21 + .../Intl/Exception/ExceptionInterface.php | 21 + .../Intl/Exception/InvalidArgumentException.php | 21 + .../MethodArgumentNotImplementedException.php | 32 + .../MethodArgumentValueNotImplementedException.php | 41 + .../Exception/MethodNotImplementedException.php | 28 + .../Intl/Exception/NotImplementedException.php | 32 + .../Intl/Exception/OutOfBoundsException.php | 21 + .../Component/Intl/Exception/RuntimeException.php | 21 + .../Symfony/Component/Intl/Globals/IntlGlobals.php | 137 ++ .../symfony/intl/Symfony/Component/Intl/Intl.php | 211 ++ vendor/symfony/intl/Symfony/Component/Intl/LICENSE | 19 + .../intl/Symfony/Component/Intl/Locale/Locale.php | 317 +++ .../Intl/NumberFormatter/NumberFormatter.php | 891 ++++++++ .../symfony/intl/Symfony/Component/Intl/README.md | 25 + .../Intl/ResourceBundle/AbstractBundle.php | 71 + .../ResourceBundle/Compiler/BundleCompiler.php | 71 + .../Compiler/BundleCompilerInterface.php | 29 + .../Intl/ResourceBundle/CurrencyBundle.php | 94 + .../ResourceBundle/CurrencyBundleInterface.php | 74 + .../Intl/ResourceBundle/LanguageBundle.php | 115 + .../ResourceBundle/LanguageBundleInterface.php | 64 + .../Component/Intl/ResourceBundle/LocaleBundle.php | 52 + .../Intl/ResourceBundle/LocaleBundleInterface.php | 41 + .../ResourceBundle/Reader/AbstractBundleReader.php | 42 + .../ResourceBundle/Reader/BinaryBundleReader.php | 51 + .../ResourceBundle/Reader/BufferedBundleReader.php | 62 + .../Reader/BundleReaderInterface.php | 40 + .../Intl/ResourceBundle/Reader/PhpBundleReader.php | 61 + .../Reader/StructuredBundleReader.php | 113 + .../Reader/StructuredBundleReaderInterface.php | 50 + .../Component/Intl/ResourceBundle/RegionBundle.php | 52 + .../Intl/ResourceBundle/RegionBundleInterface.php | 41 + .../ResourceBundle/ResourceBundleInterface.php | 27 + .../Transformer/BundleTransformer.php | 96 + .../Transformer/CompilationContext.php | 97 + .../Transformer/CompilationContextInterface.php | 56 + .../Rule/CurrencyBundleTransformationRule.php | 94 + .../Rule/LanguageBundleTransformationRule.php | 71 + .../Rule/LocaleBundleTransformationRule.php | 251 +++ .../Rule/RegionBundleTransformationRule.php | 70 + .../Rule/TransformationRuleInterface.php | 70 + .../ResourceBundle/Transformer/StubbingContext.php | 80 + .../Transformer/StubbingContextInterface.php | 46 + .../Util/ArrayAccessibleResourceBundle.php | 79 + .../ResourceBundle/Util/RecursiveArrayAccess.php | 33 + .../Intl/ResourceBundle/Util/RingBuffer.php | 88 + .../Writer/BundleWriterInterface.php | 29 + .../Intl/ResourceBundle/Writer/PhpBundleWriter.php | 50 + .../ResourceBundle/Writer/TextBundleWriter.php | 202 ++ .../Component/Intl/Resources/bin/autoload.php | 18 + .../Component/Intl/Resources/bin/common.php | 69 + .../Intl/Resources/bin/copy-stubs-to-component.php | 63 + .../Component/Intl/Resources/bin/create-stubs.php | 112 + .../Component/Intl/Resources/bin/icu-version.php | 18 + .../Symfony/Component/Intl/Resources/bin/icu.ini | 9 + .../Component/Intl/Resources/bin/test-compat.php | 56 + .../Intl/Resources/bin/update-icu-component.php | 212 ++ .../Intl/Resources/bin/util/test-compat-helper.php | 23 + .../Component/Intl/Resources/stubs/Collator.php | 21 + .../Intl/Resources/stubs/IntlDateFormatter.php | 21 + .../Component/Intl/Resources/stubs/Locale.php | 21 + .../Intl/Resources/stubs/NumberFormatter.php | 21 + .../Component/Intl/Resources/stubs/functions.php | 80 + .../Intl/Tests/Collator/AbstractCollatorTest.php | 62 + .../Component/Intl/Tests/Collator/CollatorTest.php | 109 + .../Tests/Collator/Verification/CollatorTest.php | 37 + .../AbstractIntlDateFormatterTest.php | 932 ++++++++ .../Tests/DateFormatter/IntlDateFormatterTest.php | 220 ++ .../Verification/IntlDateFormatterTest.php | 64 + .../Intl/Tests/Globals/AbstractIntlGlobalsTest.php | 41 + .../Intl/Tests/Globals/IntlGlobalsTest.php | 22 + .../Tests/Globals/Verification/IntlGlobalsTest.php | 36 + .../Intl/Tests/Locale/AbstractLocaleTest.php | 29 + .../Component/Intl/Tests/Locale/LocaleTest.php | 159 ++ .../Intl/Tests/Locale/Verification/LocaleTest.php | 38 + .../AbstractNumberFormatterTest.php | 707 +++++++ .../Tests/NumberFormatter/NumberFormatterTest.php | 239 +++ .../Verification/NumberFormatterTest.php | 54 + .../Tests/ResourceBundle/AbstractBundleTest.php | 55 + .../Tests/ResourceBundle/CurrencyBundleTest.php | 98 + .../Tests/ResourceBundle/LanguageBundleTest.php | 197 ++ .../Intl/Tests/ResourceBundle/LocaleBundleTest.php | 64 + .../Reader/AbstractBundleReaderTest.php | 64 + .../Reader/BinaryBundleReaderTest.php | 58 + .../Reader/Fixtures/NotAFile/en.php/.gitkeep | 0 .../Tests/ResourceBundle/Reader/Fixtures/en.php | 14 + .../Tests/ResourceBundle/Reader/Fixtures/en.res | Bin 0 -> 84 bytes .../Tests/ResourceBundle/Reader/Fixtures/en.txt | 3 + .../ResourceBundle/Reader/PhpBundleReaderTest.php | 63 + .../Reader/StructuredBundleReaderTest.php | 223 ++ .../Intl/Tests/ResourceBundle/RegionBundleTest.php | 63 + .../Tests/ResourceBundle/Util/RingBufferTest.php | 101 + .../Tests/ResourceBundle/Writer/Fixtures/en.php | 23 + .../Tests/ResourceBundle/Writer/Fixtures/en.res | Bin 0 -> 316 bytes .../Tests/ResourceBundle/Writer/Fixtures/en.txt | 23 + .../ResourceBundle/Writer/PhpBundleWriterTest.php | 62 + .../ResourceBundle/Writer/TextBundleWriterTest.php | 67 + .../Component/Intl/Tests/Util/IcuVersionTest.php | 111 + .../Component/Intl/Tests/Util/VersionTest.php | 87 + .../Symfony/Component/Intl/Util/IcuVersion.php | 105 + .../Symfony/Component/Intl/Util/IntlTestHelper.php | 128 ++ .../intl/Symfony/Component/Intl/Util/SvnCommit.php | 66 + .../Symfony/Component/Intl/Util/SvnRepository.php | 141 ++ .../intl/Symfony/Component/Intl/Util/Version.php | 96 + .../intl/Symfony/Component/Intl/composer.json | 48 + .../intl/Symfony/Component/Intl/phpunit.xml.dist | 29 + .../Symfony/Component/OptionsResolver/.gitignore | 4 + .../Exception/ExceptionInterface.php | 21 + .../Exception/InvalidOptionsException.php | 21 + .../Exception/MissingOptionsException.php | 21 + .../Exception/OptionDefinitionException.php | 21 + .../Symfony/Component/OptionsResolver/LICENSE | 19 + .../Symfony/Component/OptionsResolver/Options.php | 513 +++++ .../Component/OptionsResolver/OptionsResolver.php | 346 +++ .../OptionsResolver/OptionsResolverInterface.php | 210 ++ .../Symfony/Component/OptionsResolver/README.md | 107 + .../OptionsResolver/Tests/OptionsResolverTest.php | 681 ++++++ .../OptionsResolver/Tests/OptionsTest.php | 529 +++++ .../Component/OptionsResolver/composer.json | 31 + .../Component/OptionsResolver/phpunit.xml.dist | 29 + .../Component/PropertyAccess/.gitattributes | 2 + .../Symfony/Component/PropertyAccess/.gitignore | 4 + .../Symfony/Component/PropertyAccess/CHANGELOG.md | 14 + .../Exception/ExceptionInterface.php | 21 + .../Exception/InvalidPropertyPathException.php | 21 + .../Exception/NoSuchPropertyException.php | 21 + .../Exception/OutOfBoundsException.php | 21 + .../PropertyAccess/Exception/RuntimeException.php | 21 + .../Exception/UnexpectedTypeException.php | 25 + .../Symfony/Component/PropertyAccess/LICENSE | 19 + .../Component/PropertyAccess/PropertyAccess.php | 60 + .../Component/PropertyAccess/PropertyAccessor.php | 442 ++++ .../PropertyAccess/PropertyAccessorBuilder.php | 67 + .../PropertyAccess/PropertyAccessorInterface.php | 81 + .../Component/PropertyAccess/PropertyPath.php | 225 ++ .../PropertyAccess/PropertyPathBuilder.php | 306 +++ .../PropertyAccess/PropertyPathInterface.php | 86 + .../PropertyAccess/PropertyPathIterator.php | 55 + .../PropertyPathIteratorInterface.php | 34 + .../Symfony/Component/PropertyAccess/README.md | 14 + .../Component/PropertyAccess/StringUtil.php | 195 ++ .../Symfony/Component/PropertyAccess/composer.json | 31 + .../routing/Symfony/Component/Routing/.gitignore | 4 + .../Symfony/Component/Routing/Annotation/Route.php | 156 ++ .../routing/Symfony/Component/Routing/CHANGELOG.md | 162 ++ .../Symfony/Component/Routing/CompiledRoute.php | 134 ++ .../Routing/Exception/ExceptionInterface.php | 23 + .../Exception/InvalidParameterException.php | 23 + .../Exception/MethodNotAllowedException.php | 46 + .../MissingMandatoryParametersException.php | 24 + .../Exception/ResourceNotFoundException.php | 25 + .../Routing/Exception/RouteNotFoundException.php | 23 + .../ConfigurableRequirementsInterface.php | 55 + .../Routing/Generator/Dumper/GeneratorDumper.php | 45 + .../Generator/Dumper/GeneratorDumperInterface.php | 41 + .../Generator/Dumper/PhpGeneratorDumper.php | 123 ++ .../Component/Routing/Generator/UrlGenerator.php | 322 +++ .../Routing/Generator/UrlGeneratorInterface.php | 87 + .../routing/Symfony/Component/Routing/LICENSE | 19 + .../Routing/Loader/AnnotationClassLoader.php | 246 +++ .../Routing/Loader/AnnotationDirectoryLoader.php | 77 + .../Routing/Loader/AnnotationFileLoader.php | 122 ++ .../Component/Routing/Loader/ClosureLoader.php | 52 + .../Component/Routing/Loader/PhpFileLoader.php | 62 + .../Component/Routing/Loader/XmlFileLoader.php | 238 +++ .../Component/Routing/Loader/YamlFileLoader.php | 212 ++ .../Routing/Loader/schema/routing/routing-1.0.xsd | 64 + .../Component/Routing/Matcher/ApacheUrlMatcher.php | 94 + .../Routing/Matcher/Dumper/ApacheMatcherDumper.php | 252 +++ .../Routing/Matcher/Dumper/DumperCollection.php | 159 ++ .../Matcher/Dumper/DumperPrefixCollection.php | 108 + .../Routing/Matcher/Dumper/DumperRoute.php | 64 + .../Routing/Matcher/Dumper/MatcherDumper.php | 45 + .../Matcher/Dumper/MatcherDumperInterface.php | 37 + .../Routing/Matcher/Dumper/PhpMatcherDumper.php | 378 ++++ .../Routing/Matcher/RedirectableUrlMatcher.php | 61 + .../Matcher/RedirectableUrlMatcherInterface.php | 35 + .../Routing/Matcher/RequestMatcherInterface.php | 39 + .../Routing/Matcher/TraceableUrlMatcher.php | 121 ++ .../Component/Routing/Matcher/UrlMatcher.php | 208 ++ .../Routing/Matcher/UrlMatcherInterface.php | 43 + .../routing/Symfony/Component/Routing/README.md | 34 + .../Symfony/Component/Routing/RequestContext.php | 315 +++ .../Routing/RequestContextAwareInterface.php | 36 + .../routing/Symfony/Component/Routing/Route.php | 594 ++++++ .../Symfony/Component/Routing/RouteCollection.php | 271 +++ .../Symfony/Component/Routing/RouteCompiler.php | 233 ++ .../Component/Routing/RouteCompilerInterface.php | 32 + .../routing/Symfony/Component/Routing/Router.php | 289 +++ .../Symfony/Component/Routing/RouterInterface.php | 32 + .../Routing/Tests/Annotation/RouteTest.php | 49 + .../Component/Routing/Tests/CompiledRouteTest.php | 26 + .../Fixtures/AnnotatedClasses/AbstractClass.php | 16 + .../Tests/Fixtures/AnnotatedClasses/BarClass.php | 19 + .../Tests/Fixtures/AnnotatedClasses/FooClass.php | 16 + .../Routing/Tests/Fixtures/CustomXmlFileLoader.php | 26 + .../Tests/Fixtures/RedirectableUrlMatcher.php | 30 + .../Component/Routing/Tests/Fixtures/annotated.php | 0 .../Tests/Fixtures/dumper/url_matcher1.apache | 163 ++ .../Routing/Tests/Fixtures/dumper/url_matcher1.php | 310 +++ .../Tests/Fixtures/dumper/url_matcher2.apache | 7 + .../Routing/Tests/Fixtures/dumper/url_matcher2.php | 340 +++ .../Routing/Tests/Fixtures/dumper/url_matcher3.php | 43 + .../Component/Routing/Tests/Fixtures/empty.yml | 0 .../Component/Routing/Tests/Fixtures/foo.xml | 0 .../Component/Routing/Tests/Fixtures/foo1.xml | 0 .../Routing/Tests/Fixtures/incomplete.yml | 2 + .../Routing/Tests/Fixtures/missing_id.xml | 8 + .../Routing/Tests/Fixtures/missing_path.xml | 8 + .../Routing/Tests/Fixtures/namespaceprefix.xml | 13 + .../Fixtures/nonesense_resource_plus_path.yml | 3 + .../Fixtures/nonesense_type_without_resource.yml | 3 + .../Component/Routing/Tests/Fixtures/nonvalid.xml | 11 + .../Component/Routing/Tests/Fixtures/nonvalid.yml | 1 + .../Component/Routing/Tests/Fixtures/nonvalid2.yml | 1 + .../Routing/Tests/Fixtures/nonvalidkeys.yml | 3 + .../Routing/Tests/Fixtures/nonvalidnode.xml | 8 + .../Routing/Tests/Fixtures/nonvalidroute.xml | 13 + .../Routing/Tests/Fixtures/special_route_name.yml | 2 + .../Routing/Tests/Fixtures/validpattern.php | 23 + .../Routing/Tests/Fixtures/validpattern.xml | 21 + .../Routing/Tests/Fixtures/validpattern.yml | 17 + .../Routing/Tests/Fixtures/validresource.xml | 12 + .../Routing/Tests/Fixtures/validresource.yml | 7 + .../Routing/Tests/Fixtures/withdoctype.xml | 3 + .../Generator/Dumper/PhpGeneratorDumperTest.php | 117 ++ .../Routing/Tests/Generator/UrlGeneratorTest.php | 635 ++++++ .../Tests/Loader/AbstractAnnotationLoaderTest.php | 38 + .../Tests/Loader/AnnotationClassLoaderTest.php | 119 ++ .../Tests/Loader/AnnotationDirectoryLoaderTest.php | 53 + .../Tests/Loader/AnnotationFileLoaderTest.php | 47 + .../Routing/Tests/Loader/ClosureLoaderTest.php | 55 + .../Routing/Tests/Loader/PhpFileLoaderTest.php | 55 + .../Routing/Tests/Loader/XmlFileLoaderTest.php | 127 ++ .../Routing/Tests/Loader/YamlFileLoaderTest.php | 113 + .../Routing/Tests/Matcher/ApacheUrlMatcherTest.php | 137 ++ .../Matcher/Dumper/ApacheMatcherDumperTest.php | 196 ++ .../Tests/Matcher/Dumper/DumperCollectionTest.php | 33 + .../Matcher/Dumper/DumperPrefixCollectionTest.php | 123 ++ .../Tests/Matcher/Dumper/PhpMatcherDumperTest.php | 261 +++ .../Tests/Matcher/RedirectableUrlMatcherTest.php | 58 + .../Tests/Matcher/TraceableUrlMatcherTest.php | 66 + .../Routing/Tests/Matcher/UrlMatcherTest.php | 383 ++++ .../Routing/Tests/RouteCollectionTest.php | 255 +++ .../Component/Routing/Tests/RouteCompilerTest.php | 253 +++ .../Symfony/Component/Routing/Tests/RouteTest.php | 192 ++ .../Symfony/Component/Routing/Tests/RouterTest.php | 138 ++ .../Symfony/Component/Routing/composer.json | 42 + .../Symfony/Component/Routing/phpunit.xml.dist | 29 + .../Symfony/Component/Translation/.gitignore | 4 + .../Symfony/Component/Translation/CHANGELOG.md | 27 + .../Translation/Catalogue/AbstractOperation.php | 146 ++ .../Translation/Catalogue/DiffOperation.php | 49 + .../Translation/Catalogue/MergeOperation.php | 45 + .../Translation/Catalogue/OperationInterface.php | 63 + .../Component/Translation/Dumper/CsvFileDumper.php | 63 + .../Translation/Dumper/DumperInterface.php | 31 + .../Component/Translation/Dumper/FileDumper.php | 65 + .../Translation/Dumper/IcuResFileDumper.php | 135 ++ .../Component/Translation/Dumper/IniFileDumper.php | 45 + .../Component/Translation/Dumper/MoFileDumper.php | 82 + .../Component/Translation/Dumper/PhpFileDumper.php | 40 + .../Component/Translation/Dumper/PoFileDumper.php | 55 + .../Component/Translation/Dumper/QtFileDumper.php | 50 + .../Translation/Dumper/XliffFileDumper.php | 66 + .../Translation/Dumper/YamlFileDumper.php | 39 + .../Translation/Exception/ExceptionInterface.php | 23 + .../Exception/InvalidResourceException.php | 23 + .../Exception/NotFoundResourceException.php | 23 + .../Translation/Extractor/ChainExtractor.php | 60 + .../Translation/Extractor/ExtractorInterface.php | 38 + .../Component/Translation/IdentityTranslator.php | 74 + .../Symfony/Component/Translation/Interval.php | 107 + .../Symfony/Component/Translation/LICENSE | 19 + .../Component/Translation/Loader/ArrayLoader.php | 70 + .../Component/Translation/Loader/CsvFileLoader.php | 92 + .../Translation/Loader/IcuDatFileLoader.php | 54 + .../Translation/Loader/IcuResFileLoader.php | 84 + .../Component/Translation/Loader/IniFileLoader.php | 45 + .../Translation/Loader/LoaderInterface.php | 41 + .../Component/Translation/Loader/MoFileLoader.php | 179 ++ .../Component/Translation/Loader/PhpFileLoader.php | 49 + .../Component/Translation/Loader/PoFileLoader.php | 178 ++ .../Component/Translation/Loader/QtFileLoader.php | 95 + .../Translation/Loader/XliffFileLoader.php | 163 ++ .../Translation/Loader/YamlFileLoader.php | 71 + .../dic/xliff-core/xliff-core-1.2-strict.xsd | 2223 ++++++++++++++++++++ .../Loader/schema/dic/xliff-core/xml.xsd | 309 +++ .../Component/Translation/MessageCatalogue.php | 295 +++ .../Translation/MessageCatalogueInterface.php | 172 ++ .../Component/Translation/MessageSelector.php | 82 + .../Translation/MetadataAwareInterface.php | 54 + .../Component/Translation/PluralizationRules.php | 219 ++ .../Symfony/Component/Translation/README.md | 35 + .../Tests/Catalogue/AbstractOperationTest.php | 74 + .../Tests/Catalogue/DiffOperationTest.php | 60 + .../Tests/Catalogue/MergeOperationTest.php | 60 + .../Translation/Tests/Dumper/CsvFileDumperTest.php | 33 + .../Tests/Dumper/IcuResFileDumperTest.php | 37 + .../Translation/Tests/Dumper/IniFileDumperTest.php | 32 + .../Translation/Tests/Dumper/MoFileDumperTest.php | 31 + .../Translation/Tests/Dumper/PhpFileDumperTest.php | 32 + .../Translation/Tests/Dumper/PoFileDumperTest.php | 31 + .../Translation/Tests/Dumper/QtFileDumperTest.php | 32 + .../Tests/Dumper/XliffFileDumperTest.php | 32 + .../Tests/Dumper/YamlFileDumperTest.php | 39 + .../Translation/Tests/IdentityTranslatorTest.php | 61 + .../Component/Translation/Tests/IntervalTest.php | 48 + .../Translation/Tests/Loader/CsvFileLoaderTest.php | 67 + .../Tests/Loader/IcuDatFileLoaderTest.php | 72 + .../Tests/Loader/IcuResFileLoaderTest.php | 59 + .../Translation/Tests/Loader/IniFileLoaderTest.php | 57 + .../Translation/Tests/Loader/LocalizedTestCase.php | 22 + .../Translation/Tests/Loader/MoFileLoaderTest.php | 67 + .../Translation/Tests/Loader/PhpFileLoaderTest.php | 56 + .../Translation/Tests/Loader/PoFileLoaderTest.php | 79 + .../Translation/Tests/Loader/QtFileLoaderTest.php | 66 + .../Tests/Loader/XliffFileLoaderTest.php | 113 + .../Tests/Loader/YamlFileLoaderTest.php | 81 + .../Translation/Tests/MessageCatalogueTest.php | 212 ++ .../Translation/Tests/MessageSelectorTest.php | 80 + .../Translation/Tests/PluralizationRulesTest.php | 124 ++ .../Component/Translation/Tests/TranslatorTest.php | 306 +++ .../Tests/fixtures/empty-translation.po | 3 + .../Component/Translation/Tests/fixtures/empty.csv | 0 .../Component/Translation/Tests/fixtures/empty.ini | 0 .../Component/Translation/Tests/fixtures/empty.mo | 0 .../Component/Translation/Tests/fixtures/empty.po | 0 .../Component/Translation/Tests/fixtures/empty.yml | 0 .../Translation/Tests/fixtures/encoding.xlf | 15 + .../Tests/fixtures/invalid-xml-resources.xlf | 23 + .../Translation/Tests/fixtures/non-valid.xlf | 11 + .../Translation/Tests/fixtures/non-valid.yml | 1 + .../Translation/Tests/fixtures/plurals.mo | Bin 0 -> 74 bytes .../Translation/Tests/fixtures/plurals.po | 5 + .../Translation/Tests/fixtures/resname.xlf | 19 + .../resourcebundle/corrupted/resources.dat | 1 + .../Tests/fixtures/resourcebundle/dat/en.res | Bin 0 -> 120 bytes .../Tests/fixtures/resourcebundle/dat/en.txt | 3 + .../Tests/fixtures/resourcebundle/dat/fr.res | Bin 0 -> 124 bytes .../Tests/fixtures/resourcebundle/dat/fr.txt | 3 + .../fixtures/resourcebundle/dat/packagelist.txt | 2 + .../fixtures/resourcebundle/dat/resources.dat | Bin 0 -> 352 bytes .../Tests/fixtures/resourcebundle/res/en.res | Bin 0 -> 84 bytes .../Translation/Tests/fixtures/resources-clean.xlf | 15 + .../Translation/Tests/fixtures/resources.csv | 4 + .../Translation/Tests/fixtures/resources.ini | 1 + .../Translation/Tests/fixtures/resources.mo | Bin 0 -> 52 bytes .../Translation/Tests/fixtures/resources.php | 5 + .../Translation/Tests/fixtures/resources.po | 2 + .../Translation/Tests/fixtures/resources.ts | 10 + .../Translation/Tests/fixtures/resources.xlf | 23 + .../Translation/Tests/fixtures/resources.yml | 1 + .../Component/Translation/Tests/fixtures/valid.csv | 4 + .../Translation/Tests/fixtures/withdoctype.xlf | 12 + .../Symfony/Component/Translation/Translator.php | 282 +++ .../Component/Translation/TranslatorInterface.php | 69 + .../Translation/Writer/TranslationWriter.php | 73 + .../Symfony/Component/Translation/composer.json | 39 + .../Symfony/Component/Translation/phpunit.xml.dist | 29 + .../twig-bridge/Symfony/Bridge/Twig/.gitignore | 4 + .../twig-bridge/Symfony/Bridge/Twig/CHANGELOG.md | 29 + .../Bridge/Twig/Extension/CodeExtension.php | 232 ++ .../Bridge/Twig/Extension/FormExtension.php | 136 ++ .../Bridge/Twig/Extension/HttpKernelExtension.php | 88 + .../Bridge/Twig/Extension/RoutingExtension.php | 100 + .../Bridge/Twig/Extension/SecurityExtension.php | 63 + .../Bridge/Twig/Extension/TranslationExtension.php | 118 ++ .../Bridge/Twig/Extension/YamlExtension.php | 67 + .../Symfony/Bridge/Twig/Form/TwigRenderer.php | 41 + .../Bridge/Twig/Form/TwigRendererEngine.php | 183 ++ .../Twig/Form/TwigRendererEngineInterface.php | 27 + .../Bridge/Twig/Form/TwigRendererInterface.php | 27 + .../twig-bridge/Symfony/Bridge/Twig/LICENSE | 19 + .../Symfony/Bridge/Twig/Node/FormEnctypeNode.php | 31 + .../Symfony/Bridge/Twig/Node/FormThemeNode.php | 40 + .../Symfony/Bridge/Twig/Node/RenderBlockNode.php | 42 + .../Bridge/Twig/Node/SearchAndRenderBlockNode.php | 106 + .../Bridge/Twig/Node/TransDefaultDomainNode.php | 33 + .../Symfony/Bridge/Twig/Node/TransNode.php | 119 ++ .../Symfony/Bridge/Twig/NodeVisitor/Scope.php | 135 ++ .../TranslationDefaultDomainNodeVisitor.php | 106 + .../Twig/NodeVisitor/TranslationNodeVisitor.php | 137 ++ .../twig-bridge/Symfony/Bridge/Twig/README.md | 15 + .../Resources/views/Form/form_div_layout.html.twig | 390 ++++ .../views/Form/form_table_layout.html.twig | 52 + .../Twig/Tests/Extension/CodeExtensionTest.php | 69 + .../Extension/Fixtures/StubFilesystemLoader.php | 30 + .../Tests/Extension/Fixtures/StubTranslator.php | 35 + .../Tests/Extension/FormExtensionDivLayoutTest.php | 209 ++ .../Extension/FormExtensionTableLayoutTest.php | 131 ++ .../Tests/Extension/HttpKernelExtensionTest.php | 68 + .../Twig/Tests/Extension/RoutingExtensionTest.php | 60 + .../Tests/Extension/TranslationExtensionTest.php | 151 ++ .../Twig/Tests/Extension/child_label.html.twig | 3 + .../Twig/Tests/Extension/custom_widgets.html.twig | 16 + .../Twig/Tests/Extension/parent_label.html.twig | 3 + .../Bridge/Twig/Tests/Extension/theme.html.twig | 6 + .../Twig/Tests/Extension/theme_extends.html.twig | 8 + .../Twig/Tests/Extension/theme_use.html.twig | 8 + .../Bridge/Twig/Tests/Node/FormThemeTest.php | 85 + .../Tests/Node/SearchAndRenderBlockNodeTest.php | 282 +++ .../Bridge/Twig/Tests/NodeVisitor/ScopeTest.php | 25 + .../TranslationDefaultDomainNodeVisitorTest.php | 83 + .../NodeVisitor/TranslationNodeVisitorTest.php | 61 + .../Twig/Tests/NodeVisitor/TwigNodeProvider.php | 77 + .../Symfony/Bridge/Twig/Tests/TestCase.php | 22 + .../Tests/TokenParser/FormThemeTokenParserTest.php | 108 + .../Twig/Tests/Translation/TwigExtractorTest.php | 81 + .../Twig/TokenParser/FormThemeTokenParser.php | 61 + .../Twig/TokenParser/TransChoiceTokenParser.php | 89 + .../TokenParser/TransDefaultDomainTokenParser.php | 48 + .../Bridge/Twig/TokenParser/TransTokenParser.php | 89 + .../Bridge/Twig/Translation/TwigExtractor.php | 86 + .../twig-bridge/Symfony/Bridge/Twig/TwigEngine.php | 126 ++ .../twig-bridge/Symfony/Bridge/Twig/composer.json | 50 + .../Symfony/Bridge/Twig/phpunit.xml.dist | 30 + 796 files changed, 87766 insertions(+) create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.gitignore create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/CHANGELOG.md create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Event.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcher.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcherInterface.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventSubscriberInterface.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/GenericEvent.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/LICENSE create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/README.md create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventTest.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/composer.json create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/phpunit.xml.dist create mode 100644 vendor/symfony/filesystem/Symfony/Component/Filesystem/.gitignore create mode 100644 vendor/symfony/filesystem/Symfony/Component/Filesystem/CHANGELOG.md create mode 100644 vendor/symfony/filesystem/Symfony/Component/Filesystem/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/filesystem/Symfony/Component/Filesystem/Exception/IOException.php create mode 100644 vendor/symfony/filesystem/Symfony/Component/Filesystem/Filesystem.php create mode 100644 vendor/symfony/filesystem/Symfony/Component/Filesystem/LICENSE create mode 100644 vendor/symfony/filesystem/Symfony/Component/Filesystem/README.md create mode 100644 vendor/symfony/filesystem/Symfony/Component/Filesystem/Tests/FilesystemTest.php create mode 100644 vendor/symfony/filesystem/Symfony/Component/Filesystem/composer.json create mode 100644 vendor/symfony/filesystem/Symfony/Component/Filesystem/phpunit.xml.dist create mode 100644 vendor/symfony/form/Symfony/Component/Form/.gitignore create mode 100644 vendor/symfony/form/Symfony/Component/Form/AbstractExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/AbstractRendererEngine.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/AbstractType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/AbstractTypeExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Button.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/ButtonBuilder.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/ButtonTypeInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/CHANGELOG.md create mode 100644 vendor/symfony/form/Symfony/Component/Form/CallbackTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/ClickableInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/DataMapperInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/DataTransformerInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/AlreadyBoundException.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/AlreadySubmittedException.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/BadMethodCallException.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/ErrorMappingException.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/InvalidArgumentException.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/InvalidConfigurationException.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/LogicException.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/OutOfBoundsException.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/RuntimeException.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/StringCastException.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/TransformationFailedException.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/UnexpectedTypeException.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceList.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceListInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/LazyChoiceList.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ObjectChoiceList.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/SimpleChoiceList.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/CoreExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ArrayToPartsTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/BaseDateTimeTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/BooleanToStringTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ChoiceToBooleanArrayTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ChoiceToValueTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ChoicesToBooleanArrayTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ChoicesToValuesTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DataTransformerChain.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToRfc3339Transformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToTimestampTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ValueToDuplicatesTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/FixCheckboxInputListener.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/FixRadioInputListener.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/FixUrlProtocolListener.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/MergeCollectionListener.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/ResizeFormListener.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/TrimListener.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/BaseType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/BirthdayType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/ButtonType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/CollectionType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/CountryType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/DateType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/EmailType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/FileType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/FormType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/HiddenType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/IntegerType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/LanguageType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/LocaleType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/MoneyType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/NumberType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/PasswordType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/PercentType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/RadioType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/RepeatedType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/ResetType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/SearchType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/SubmitType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/TextType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/TextareaType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/TimeType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/UrlType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/View/ChoiceView.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/CsrfProvider/CsrfProviderInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/CsrfProvider/SessionCsrfProvider.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/HttpFoundation/EventListener/BindRequestListener.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationRequestHandler.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/HttpFoundation/Type/FormTypeHttpFoundationExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Templating/TemplatingExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Templating/TemplatingRendererEngine.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Constraints/Form.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/BaseValidatorExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/RepeatedTypeValidatorExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/SubmitTypeValidatorExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Util/ServerParams.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/RelativePath.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapperInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPathIterator.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Form.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormBuilder.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormBuilderInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormConfigBuilder.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormConfigBuilderInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormConfigInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormError.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormEvent.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormEvents.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormExtensionInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormFactory.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormFactoryBuilder.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormFactoryBuilderInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormFactoryInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormRegistry.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormRegistryInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormRenderer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormRendererEngineInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormRendererInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormTypeExtensionInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormTypeGuesserChain.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormTypeGuesserInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormTypeInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormView.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Forms.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Guess/Guess.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Guess/TypeGuess.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Guess/ValueGuess.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/LICENSE create mode 100644 vendor/symfony/form/Symfony/Component/Form/NativeRequestHandler.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/PreloadedExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/README.md create mode 100644 vendor/symfony/form/Symfony/Component/Form/RequestHandlerInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/ResolvedFormType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/ResolvedFormTypeFactory.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/ResolvedFormTypeFactoryInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/ResolvedFormTypeInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/config/validation.xml create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ar.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.bg.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ca.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.cs.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.da.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.de.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.el.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.en.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.es.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.et.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.eu.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.fa.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.fi.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.fr.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.gl.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.he.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.hr.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.hu.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.hy.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.id.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.it.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ja.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.lb.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.lt.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.lv.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.mn.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.nb.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.nl.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.pl.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.pt.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.pt_BR.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ro.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ru.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sk.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sl.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sr_Cyrl.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sr_Latn.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sv.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ua.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.zh_CN.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/ReversedTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/SubmitButton.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/SubmitButtonBuilder.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/SubmitButtonTypeInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Test/DeprecationErrorHandler.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Test/FormBuilderInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Test/FormIntegrationTestCase.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Test/FormInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Test/FormPerformanceTestCase.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Test/TypeTestCase.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/AbstractExtensionTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/AbstractFormTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/AbstractLayoutTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/AbstractRequestHandlerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/CompoundFormPerformanceTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/CompoundFormTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/ChoiceListTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/LazyChoiceListTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/ObjectChoiceListTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/SimpleChoiceListTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ArrayToPartsTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BooleanToStringTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoiceToValueTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoicesToValuesTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DataTransformerChainTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeTestCase.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToArrayTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToRfc3339TransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToStringTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToTimestampTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/PercentToLocalizedStringTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ValueToDuplicatesTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixRadioInputListenerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixUrlProtocolListenerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/Fixtures/randomhash create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayObjectTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerCustomArrayObjectTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/ResizeFormListenerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/TrimListenerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/BaseTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/ButtonTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypePerformanceTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/CountryTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/CurrencyTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/LanguageTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/LocaleTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/MoneyTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/PasswordTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/SubmitTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/TimezoneTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/TypeTestCase.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/UrlTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Csrf/CsrfProvider/DefaultCsrfProviderTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Csrf/CsrfProvider/SessionCsrfProviderTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/CsrfValidationListenerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/HttpFoundation/EventListener/BindRequestListenerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/EventListener/ValidationListenerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/Type/TypeTestCase.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/Util/ServerParamsTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationPathTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/AlternatingRowType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/Author.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/AuthorType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/CustomArrayObject.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FixedDataTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FixedFilterListener.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooSubType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooSubTypeWithParentInstance.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooTypeBarExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooTypeBazExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/TestExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/foo create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/FormBuilderTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/FormConfigTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/FormFactoryBuilderTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/FormFactoryTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/FormIntegrationTestCase.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/FormPerformanceTestCase.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/FormRegistryTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/FormRendererTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Guess/GuessTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/NativeRequestHandlerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/ResolvedFormTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/SimpleFormTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Util/FormUtil.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Util/InheritDataAwareIterator.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Util/VirtualFormAwareIterator.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/composer.json create mode 100644 vendor/symfony/form/Symfony/Component/Form/phpunit.xml.dist create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/.gitignore create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/IcuCurrencyBundle.php create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/IcuData.php create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/IcuLanguageBundle.php create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/IcuLocaleBundle.php create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/IcuRegionBundle.php create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/LICENSE create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/README.md create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en.php create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/en.php create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/en.php create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/en.php create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/Resources/data/version.txt create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/Tests/IcuIntegrationTest.php create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/composer.json create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/phpunit.xml.dist create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/.gitignore create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/CONTRIBUTING.md create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Collator/Collator.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/AmPmTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfWeekTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfYearTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/DayTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/FullTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1200Transformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1201Transformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2400Transformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2401Transformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/HourTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/MinuteTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/MonthTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/QuarterTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/SecondTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/TimeZoneTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Transformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/YearTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/IntlDateFormatter.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Exception/BadMethodCallException.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Exception/InvalidArgumentException.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Exception/MethodArgumentNotImplementedException.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Exception/MethodArgumentValueNotImplementedException.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Exception/MethodNotImplementedException.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Exception/NotImplementedException.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Exception/OutOfBoundsException.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Exception/RuntimeException.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Globals/IntlGlobals.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Intl.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/LICENSE create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Locale/Locale.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/README.md create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/AbstractBundle.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Compiler/BundleCompiler.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Compiler/BundleCompilerInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/CurrencyBundle.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/CurrencyBundleInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/LanguageBundle.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/LanguageBundleInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/LocaleBundle.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/LocaleBundleInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/AbstractBundleReader.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/BinaryBundleReader.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/BufferedBundleReader.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/BundleReaderInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/PhpBundleReader.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReader.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReaderInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/RegionBundle.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/RegionBundleInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/ResourceBundleInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/BundleTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContext.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContextInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/CurrencyBundleTransformationRule.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LanguageBundleTransformationRule.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LocaleBundleTransformationRule.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/RegionBundleTransformationRule.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/TransformationRuleInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContext.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContextInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Util/ArrayAccessibleResourceBundle.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Util/RecursiveArrayAccess.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Util/RingBuffer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Writer/BundleWriterInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Writer/PhpBundleWriter.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Writer/TextBundleWriter.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/autoload.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/common.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/copy-stubs-to-component.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/create-stubs.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/icu-version.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/icu.ini create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/test-compat.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/update-icu-component.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/util/test-compat-helper.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/stubs/Collator.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/stubs/IntlDateFormatter.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/stubs/Locale.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/stubs/NumberFormatter.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/stubs/functions.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/Collator/AbstractCollatorTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/Collator/CollatorTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/Collator/Verification/CollatorTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/DateFormatter/AbstractIntlDateFormatterTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/DateFormatter/IntlDateFormatterTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/DateFormatter/Verification/IntlDateFormatterTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/Globals/AbstractIntlGlobalsTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/Globals/IntlGlobalsTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/Globals/Verification/IntlGlobalsTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/Locale/AbstractLocaleTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/Locale/LocaleTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/Locale/Verification/LocaleTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/NumberFormatter/AbstractNumberFormatterTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/NumberFormatter/NumberFormatterTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/NumberFormatter/Verification/NumberFormatterTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/AbstractBundleTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/CurrencyBundleTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/LanguageBundleTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/LocaleBundleTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/AbstractBundleReaderTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/BinaryBundleReaderTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/NotAFile/en.php/.gitkeep create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/en.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/en.res create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/en.txt create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/PhpBundleReaderTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/StructuredBundleReaderTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/RegionBundleTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Util/RingBufferTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/en.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/en.res create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/en.txt create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/PhpBundleWriterTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/TextBundleWriterTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/Util/IcuVersionTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/Util/VersionTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Util/IcuVersion.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Util/IntlTestHelper.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Util/SvnCommit.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Util/SvnRepository.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Util/Version.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/composer.json create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/phpunit.xml.dist create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/.gitignore create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/InvalidOptionsException.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/MissingOptionsException.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/OptionDefinitionException.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/LICENSE create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Options.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/OptionsResolver.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/OptionsResolverInterface.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/README.md create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Tests/OptionsTest.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/composer.json create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/phpunit.xml.dist create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/.gitattributes create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/.gitignore create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/CHANGELOG.md create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/InvalidPropertyPathException.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/NoSuchPropertyException.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/OutOfBoundsException.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/RuntimeException.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/LICENSE create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccess.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessor.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessorBuilder.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessorInterface.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPath.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathBuilder.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathInterface.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathIterator.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathIteratorInterface.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/README.md create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/StringUtil.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/composer.json create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/.gitignore create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Annotation/Route.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/CHANGELOG.md create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/CompiledRoute.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Exception/InvalidParameterException.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Exception/MethodNotAllowedException.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Exception/MissingMandatoryParametersException.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Exception/ResourceNotFoundException.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Exception/RouteNotFoundException.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Generator/ConfigurableRequirementsInterface.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Generator/Dumper/GeneratorDumper.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Generator/Dumper/GeneratorDumperInterface.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Generator/UrlGenerator.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Generator/UrlGeneratorInterface.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/LICENSE create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Loader/AnnotationClassLoader.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Loader/AnnotationDirectoryLoader.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Loader/AnnotationFileLoader.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Loader/ClosureLoader.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Loader/PhpFileLoader.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Loader/XmlFileLoader.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Loader/YamlFileLoader.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/ApacheUrlMatcher.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/ApacheMatcherDumper.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/DumperCollection.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/DumperPrefixCollection.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/DumperRoute.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/MatcherDumper.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/MatcherDumperInterface.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/RedirectableUrlMatcher.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/RedirectableUrlMatcherInterface.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/RequestMatcherInterface.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/TraceableUrlMatcher.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/UrlMatcher.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/UrlMatcherInterface.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/README.md create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/RequestContext.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/RequestContextAwareInterface.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Route.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/RouteCollection.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/RouteCompiler.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/RouteCompilerInterface.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Router.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/RouterInterface.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Annotation/RouteTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/CompiledRouteTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/AbstractClass.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/BarClass.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/FooClass.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/CustomXmlFileLoader.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/RedirectableUrlMatcher.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/annotated.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.apache create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.apache create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/empty.yml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/foo.xml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/foo1.xml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/incomplete.yml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/missing_id.xml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/missing_path.xml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/namespaceprefix.xml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonesense_resource_plus_path.yml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonesense_type_without_resource.yml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalid.xml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalid.yml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalid2.yml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalidkeys.yml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalidnode.xml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalidroute.xml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/special_route_name.yml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validpattern.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validpattern.xml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validpattern.yml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validresource.xml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validresource.yml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/withdoctype.xml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/AbstractAnnotationLoaderTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/AnnotationDirectoryLoaderTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/ClosureLoaderTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/ApacheUrlMatcherTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/Dumper/ApacheMatcherDumperTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/Dumper/DumperCollectionTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/Dumper/DumperPrefixCollectionTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/TraceableUrlMatcherTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/RouteCollectionTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/RouteCompilerTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/RouteTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/RouterTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/composer.json create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/phpunit.xml.dist create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/.gitignore create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/CHANGELOG.md create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Catalogue/AbstractOperation.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Catalogue/DiffOperation.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Catalogue/MergeOperation.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Catalogue/OperationInterface.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Dumper/CsvFileDumper.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Dumper/DumperInterface.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Dumper/FileDumper.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Dumper/IcuResFileDumper.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Dumper/IniFileDumper.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Dumper/MoFileDumper.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Dumper/PhpFileDumper.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Dumper/PoFileDumper.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Dumper/QtFileDumper.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Dumper/XliffFileDumper.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Dumper/YamlFileDumper.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Exception/InvalidResourceException.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Exception/NotFoundResourceException.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Extractor/ChainExtractor.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Extractor/ExtractorInterface.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/IdentityTranslator.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Interval.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/LICENSE create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/ArrayLoader.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/CsvFileLoader.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/IcuDatFileLoader.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/IcuResFileLoader.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/IniFileLoader.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/LoaderInterface.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/MoFileLoader.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/PhpFileLoader.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/PoFileLoader.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/QtFileLoader.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/XliffFileLoader.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/YamlFileLoader.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/schema/dic/xliff-core/xliff-core-1.2-strict.xsd create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/schema/dic/xliff-core/xml.xsd create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/MessageCatalogue.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/MessageCatalogueInterface.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/MessageSelector.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/MetadataAwareInterface.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/PluralizationRules.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/README.md create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Catalogue/AbstractOperationTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Catalogue/DiffOperationTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Catalogue/MergeOperationTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/CsvFileDumperTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/IcuResFileDumperTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/IniFileDumperTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/MoFileDumperTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/PhpFileDumperTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/PoFileDumperTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/QtFileDumperTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/XliffFileDumperTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/YamlFileDumperTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/IdentityTranslatorTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/IntervalTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/CsvFileLoaderTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/IcuDatFileLoaderTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/IcuResFileLoaderTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/IniFileLoaderTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/LocalizedTestCase.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/MoFileLoaderTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/PhpFileLoaderTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/PoFileLoaderTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/QtFileLoaderTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/XliffFileLoaderTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/YamlFileLoaderTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/MessageCatalogueTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/MessageSelectorTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/PluralizationRulesTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/TranslatorTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty-translation.po create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty.csv create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty.ini create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty.mo create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty.po create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty.yml create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/encoding.xlf create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/invalid-xml-resources.xlf create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/non-valid.xlf create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/non-valid.yml create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/plurals.mo create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/plurals.po create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resname.xlf create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/corrupted/resources.dat create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/en.res create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/en.txt create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/fr.res create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/fr.txt create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/packagelist.txt create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/resources.dat create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/res/en.res create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources-clean.xlf create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.csv create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.ini create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.mo create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.po create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.ts create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.xlf create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.yml create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/valid.csv create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/withdoctype.xlf create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Translator.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/TranslatorInterface.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Writer/TranslationWriter.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/composer.json create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/phpunit.xml.dist create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/.gitignore create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/CHANGELOG.md create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/CodeExtension.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/FormExtension.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/HttpKernelExtension.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/RoutingExtension.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/SecurityExtension.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/TranslationExtension.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/YamlExtension.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Form/TwigRenderer.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Form/TwigRendererEngine.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Form/TwigRendererEngineInterface.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Form/TwigRendererInterface.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/LICENSE create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/FormEnctypeNode.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/FormThemeNode.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/RenderBlockNode.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/SearchAndRenderBlockNode.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/TransDefaultDomainNode.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/TransNode.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/NodeVisitor/Scope.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/NodeVisitor/TranslationNodeVisitor.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/README.md create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/CodeExtensionTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubFilesystemLoader.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubTranslator.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/RoutingExtensionTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/child_label.html.twig create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/custom_widgets.html.twig create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/parent_label.html.twig create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/theme.html.twig create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/theme_extends.html.twig create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/theme_use.html.twig create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Node/SearchAndRenderBlockNodeTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/NodeVisitor/ScopeTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationDefaultDomainNodeVisitorTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationNodeVisitorTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/NodeVisitor/TwigNodeProvider.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/TestCase.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/TokenParser/FormThemeTokenParser.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/TokenParser/TransChoiceTokenParser.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/TokenParser/TransDefaultDomainTokenParser.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/TokenParser/TransTokenParser.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Translation/TwigExtractor.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/TwigEngine.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/composer.json create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/phpunit.xml.dist (limited to 'vendor/symfony') diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.gitignore b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.gitignore new file mode 100644 index 00000000..44de97a3 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.gitignore @@ -0,0 +1,4 @@ +vendor/ +composer.lock +phpunit.xml + diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/CHANGELOG.md b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/CHANGELOG.md new file mode 100644 index 00000000..536c5ac7 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/CHANGELOG.md @@ -0,0 +1,16 @@ +CHANGELOG +========= + +2.1.0 +----- + + * added TraceableEventDispatcherInterface + * added ContainerAwareEventDispatcher + * added a reference to the EventDispatcher on the Event + * added a reference to the Event name on the event + * added fluid interface to the dispatch() method which now returns the Event + object + * added GenericEvent event class + * added the possibility for subscribers to subscribe several times for the + same event + * added ImmutableEventDispatcher diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php new file mode 100644 index 00000000..9448ed43 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php @@ -0,0 +1,202 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Lazily loads listeners and subscribers from the dependency injection + * container + * + * @author Fabien Potencier + * @author Bernhard Schussek + * @author Jordan Alliot + */ +class ContainerAwareEventDispatcher extends EventDispatcher +{ + /** + * The container from where services are loaded + * @var ContainerInterface + */ + private $container; + + /** + * The service IDs of the event listeners and subscribers + * @var array + */ + private $listenerIds = array(); + + /** + * The services registered as listeners + * @var array + */ + private $listeners = array(); + + /** + * Constructor. + * + * @param ContainerInterface $container A ContainerInterface instance + */ + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + /** + * Adds a service as event listener + * + * @param string $eventName Event for which the listener is added + * @param array $callback The service ID of the listener service & the method + * name that has to be called + * @param integer $priority The higher this value, the earlier an event listener + * will be triggered in the chain. + * Defaults to 0. + * + * @throws \InvalidArgumentException + */ + public function addListenerService($eventName, $callback, $priority = 0) + { + if (!is_array($callback) || 2 !== count($callback)) { + throw new \InvalidArgumentException('Expected an array("service", "method") argument'); + } + + $this->listenerIds[$eventName][] = array($callback[0], $callback[1], $priority); + } + + public function removeListener($eventName, $listener) + { + $this->lazyLoad($eventName); + + if (isset($this->listeners[$eventName])) { + foreach ($this->listeners[$eventName] as $key => $l) { + foreach ($this->listenerIds[$eventName] as $i => $args) { + list($serviceId, $method, $priority) = $args; + if ($key === $serviceId.'.'.$method) { + if ($listener === array($l, $method)) { + unset($this->listeners[$eventName][$key]); + if (empty($this->listeners[$eventName])) { + unset($this->listeners[$eventName]); + } + unset($this->listenerIds[$eventName][$i]); + if (empty($this->listenerIds[$eventName])) { + unset($this->listenerIds[$eventName]); + } + } + } + } + } + } + + parent::removeListener($eventName, $listener); + } + + /** + * @see EventDispatcherInterface::hasListeners + */ + public function hasListeners($eventName = null) + { + if (null === $eventName) { + return (Boolean) count($this->listenerIds) || (Boolean) count($this->listeners); + } + + if (isset($this->listenerIds[$eventName])) { + return true; + } + + return parent::hasListeners($eventName); + } + + /** + * @see EventDispatcherInterface::getListeners + */ + public function getListeners($eventName = null) + { + if (null === $eventName) { + foreach (array_keys($this->listenerIds) as $serviceEventName) { + $this->lazyLoad($serviceEventName); + } + } else { + $this->lazyLoad($eventName); + } + + return parent::getListeners($eventName); + } + + /** + * Adds a service as event subscriber + * + * @param string $serviceId The service ID of the subscriber service + * @param string $class The service's class name (which must implement EventSubscriberInterface) + */ + public function addSubscriberService($serviceId, $class) + { + foreach ($class::getSubscribedEvents() as $eventName => $params) { + if (is_string($params)) { + $this->listenerIds[$eventName][] = array($serviceId, $params, 0); + } elseif (is_string($params[0])) { + $this->listenerIds[$eventName][] = array($serviceId, $params[0], isset($params[1]) ? $params[1] : 0); + } else { + foreach ($params as $listener) { + $this->listenerIds[$eventName][] = array($serviceId, $listener[0], isset($listener[1]) ? $listener[1] : 0); + } + } + } + } + + /** + * {@inheritDoc} + * + * Lazily loads listeners for this event from the dependency injection + * container. + * + * @throws \InvalidArgumentException if the service is not defined + */ + public function dispatch($eventName, Event $event = null) + { + $this->lazyLoad($eventName); + + return parent::dispatch($eventName, $event); + } + + public function getContainer() + { + return $this->container; + } + + /** + * Lazily loads listeners for this event from the dependency injection + * container. + * + * @param string $eventName The name of the event to dispatch. The name of + * the event is the name of the method that is + * invoked on listeners. + */ + protected function lazyLoad($eventName) + { + if (isset($this->listenerIds[$eventName])) { + foreach ($this->listenerIds[$eventName] as $args) { + list($serviceId, $method, $priority) = $args; + $listener = $this->container->get($serviceId); + + $key = $serviceId.'.'.$method; + if (!isset($this->listeners[$eventName][$key])) { + $this->addListener($eventName, array($listener, $method), $priority); + } elseif ($listener !== $this->listeners[$eventName][$key]) { + parent::removeListener($eventName, array($this->listeners[$eventName][$key], $method)); + $this->addListener($eventName, array($listener, $method), $priority); + } + + $this->listeners[$eventName][$key] = $listener; + } + } + } +} diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php new file mode 100644 index 00000000..a67a9790 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Debug; + +/** + * @author Fabien Potencier + */ +interface TraceableEventDispatcherInterface +{ + /** + * Gets the called listeners. + * + * @return array An array of called listeners + */ + public function getCalledListeners(); + + /** + * Gets the not called listeners. + * + * @return array An array of not called listeners + */ + public function getNotCalledListeners(); +} diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Event.php b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Event.php new file mode 100644 index 00000000..42f09eaa --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Event.php @@ -0,0 +1,121 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * Event is the base class for classes containing event data. + * + * This class contains no event data. It is used by events that do not pass + * state information to an event handler when an event is raised. + * + * You can call the method stopPropagation() to abort the execution of + * further listeners in your event listener. + * + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + * + * @api + */ +class Event +{ + /** + * @var Boolean Whether no further event listeners should be triggered + */ + private $propagationStopped = false; + + /** + * @var EventDispatcher Dispatcher that dispatched this event + */ + private $dispatcher; + + /** + * @var string This event's name + */ + private $name; + + /** + * Returns whether further event listeners should be triggered. + * + * @see Event::stopPropagation + * @return Boolean Whether propagation was already stopped for this event. + * + * @api + */ + public function isPropagationStopped() + { + return $this->propagationStopped; + } + + /** + * Stops the propagation of the event to further event listeners. + * + * If multiple event listeners are connected to the same event, no + * further event listener will be triggered once any trigger calls + * stopPropagation(). + * + * @api + */ + public function stopPropagation() + { + $this->propagationStopped = true; + } + + /** + * Stores the EventDispatcher that dispatches this Event + * + * @param EventDispatcherInterface $dispatcher + * + * @api + */ + public function setDispatcher(EventDispatcherInterface $dispatcher) + { + $this->dispatcher = $dispatcher; + } + + /** + * Returns the EventDispatcher that dispatches this Event + * + * @return EventDispatcherInterface + * + * @api + */ + public function getDispatcher() + { + return $this->dispatcher; + } + + /** + * Gets the event's name. + * + * @return string + * + * @api + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the event's name property. + * + * @param string $name The event name. + * + * @api + */ + public function setName($name) + { + $this->name = $name; + } +} diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcher.php b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcher.php new file mode 100644 index 00000000..eb1fb594 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcher.php @@ -0,0 +1,185 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * The EventDispatcherInterface is the central point of Symfony's event listener system. + * + * Listeners are registered on the manager and events are dispatched through the + * manager. + * + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + * @author Fabien Potencier + * @author Jordi Boggiano + * @author Jordan Alliot + * + * @api + */ +class EventDispatcher implements EventDispatcherInterface +{ + private $listeners = array(); + private $sorted = array(); + + /** + * @see EventDispatcherInterface::dispatch + * + * @api + */ + public function dispatch($eventName, Event $event = null) + { + if (null === $event) { + $event = new Event(); + } + + $event->setDispatcher($this); + $event->setName($eventName); + + if (!isset($this->listeners[$eventName])) { + return $event; + } + + $this->doDispatch($this->getListeners($eventName), $eventName, $event); + + return $event; + } + + /** + * @see EventDispatcherInterface::getListeners + */ + public function getListeners($eventName = null) + { + if (null !== $eventName) { + if (!isset($this->sorted[$eventName])) { + $this->sortListeners($eventName); + } + + return $this->sorted[$eventName]; + } + + foreach (array_keys($this->listeners) as $eventName) { + if (!isset($this->sorted[$eventName])) { + $this->sortListeners($eventName); + } + } + + return $this->sorted; + } + + /** + * @see EventDispatcherInterface::hasListeners + */ + public function hasListeners($eventName = null) + { + return (Boolean) count($this->getListeners($eventName)); + } + + /** + * @see EventDispatcherInterface::addListener + * + * @api + */ + public function addListener($eventName, $listener, $priority = 0) + { + $this->listeners[$eventName][$priority][] = $listener; + unset($this->sorted[$eventName]); + } + + /** + * @see EventDispatcherInterface::removeListener + */ + public function removeListener($eventName, $listener) + { + if (!isset($this->listeners[$eventName])) { + return; + } + + foreach ($this->listeners[$eventName] as $priority => $listeners) { + if (false !== ($key = array_search($listener, $listeners, true))) { + unset($this->listeners[$eventName][$priority][$key], $this->sorted[$eventName]); + } + } + } + + /** + * @see EventDispatcherInterface::addSubscriber + * + * @api + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { + if (is_string($params)) { + $this->addListener($eventName, array($subscriber, $params)); + } elseif (is_string($params[0])) { + $this->addListener($eventName, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0); + } else { + foreach ($params as $listener) { + $this->addListener($eventName, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0); + } + } + } + } + + /** + * @see EventDispatcherInterface::removeSubscriber + */ + public function removeSubscriber(EventSubscriberInterface $subscriber) + { + foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { + if (is_array($params) && is_array($params[0])) { + foreach ($params as $listener) { + $this->removeListener($eventName, array($subscriber, $listener[0])); + } + } else { + $this->removeListener($eventName, array($subscriber, is_string($params) ? $params : $params[0])); + } + } + } + + /** + * Triggers the listeners of an event. + * + * This method can be overridden to add functionality that is executed + * for each listener. + * + * @param array[callback] $listeners The event listeners. + * @param string $eventName The name of the event to dispatch. + * @param Event $event The event object to pass to the event handlers/listeners. + */ + protected function doDispatch($listeners, $eventName, Event $event) + { + foreach ($listeners as $listener) { + call_user_func($listener, $event); + if ($event->isPropagationStopped()) { + break; + } + } + } + + /** + * Sorts the internal list of listeners for the given event by priority. + * + * @param string $eventName The name of the event. + */ + private function sortListeners($eventName) + { + $this->sorted[$eventName] = array(); + + if (isset($this->listeners[$eventName])) { + krsort($this->listeners[$eventName]); + $this->sorted[$eventName] = call_user_func_array('array_merge', $this->listeners[$eventName]); + } + } +} diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcherInterface.php b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcherInterface.php new file mode 100644 index 00000000..7aead23b --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcherInterface.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * The EventDispatcherInterface is the central point of Symfony's event listener system. + * Listeners are registered on the manager and events are dispatched through the + * manager. + * + * @author Bernhard Schussek + * + * @api + */ +interface EventDispatcherInterface +{ + /** + * Dispatches an event to all registered listeners. + * + * @param string $eventName The name of the event to dispatch. The name of + * the event is the name of the method that is + * invoked on listeners. + * @param Event $event The event to pass to the event handlers/listeners. + * If not supplied, an empty Event instance is created. + * + * @return Event + * + * @api + */ + public function dispatch($eventName, Event $event = null); + + /** + * Adds an event listener that listens on the specified events. + * + * @param string $eventName The event to listen on + * @param callable $listener The listener + * @param integer $priority The higher this value, the earlier an event + * listener will be triggered in the chain (defaults to 0) + * + * @api + */ + public function addListener($eventName, $listener, $priority = 0); + + /** + * Adds an event subscriber. + * + * The subscriber is asked for all the events he is + * interested in and added as a listener for these events. + * + * @param EventSubscriberInterface $subscriber The subscriber. + * + * @api + */ + public function addSubscriber(EventSubscriberInterface $subscriber); + + /** + * Removes an event listener from the specified events. + * + * @param string|array $eventName The event(s) to remove a listener from + * @param callable $listener The listener to remove + */ + public function removeListener($eventName, $listener); + + /** + * Removes an event subscriber. + * + * @param EventSubscriberInterface $subscriber The subscriber + */ + public function removeSubscriber(EventSubscriberInterface $subscriber); + + /** + * Gets the listeners of a specific event or all listeners. + * + * @param string $eventName The name of the event + * + * @return array The event listeners for the specified event, or all event listeners by event name + */ + public function getListeners($eventName = null); + + /** + * Checks whether an event has any registered listeners. + * + * @param string $eventName The name of the event + * + * @return Boolean true if the specified event has any listeners, false otherwise + */ + public function hasListeners($eventName = null); +} diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventSubscriberInterface.php b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventSubscriberInterface.php new file mode 100644 index 00000000..080f892f --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventSubscriberInterface.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * An EventSubscriber knows himself what events he is interested in. + * If an EventSubscriber is added to an EventDispatcherInterface, the manager invokes + * {@link getSubscribedEvents} and registers the subscriber as a listener for all + * returned events. + * + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + * + * @api + */ +interface EventSubscriberInterface +{ + /** + * Returns an array of event names this subscriber wants to listen to. + * + * The array keys are event names and the value can be: + * + * * The method name to call (priority defaults to 0) + * * An array composed of the method name to call and the priority + * * An array of arrays composed of the method names to call and respective + * priorities, or 0 if unset + * + * For instance: + * + * * array('eventName' => 'methodName') + * * array('eventName' => array('methodName', $priority)) + * * array('eventName' => array(array('methodName1', $priority), array('methodName2')) + * + * @return array The event names to listen to + * + * @api + */ + public static function getSubscribedEvents(); +} diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/GenericEvent.php b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/GenericEvent.php new file mode 100644 index 00000000..3a5efcfe --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/GenericEvent.php @@ -0,0 +1,186 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * Event encapsulation class. + * + * Encapsulates events thus decoupling the observer from the subject they encapsulate. + * + * @author Drak + */ +class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate +{ + /** + * Observer pattern subject. + * + * @var mixed usually object or callable + */ + protected $subject; + + /** + * Array of arguments. + * + * @var array + */ + protected $arguments; + + /** + * Encapsulate an event with $subject and $args. + * + * @param mixed $subject The subject of the event, usually an object. + * @param array $arguments Arguments to store in the event. + */ + public function __construct($subject = null, array $arguments = array()) + { + $this->subject = $subject; + $this->arguments = $arguments; + } + + /** + * Getter for subject property. + * + * @return mixed $subject The observer subject. + */ + public function getSubject() + { + return $this->subject; + } + + /** + * Get argument by key. + * + * @param string $key Key. + * + * @throws \InvalidArgumentException If key is not found. + * + * @return mixed Contents of array key. + */ + public function getArgument($key) + { + if ($this->hasArgument($key)) { + return $this->arguments[$key]; + } + + throw new \InvalidArgumentException(sprintf('%s not found in %s', $key, $this->getName())); + } + + /** + * Add argument to event. + * + * @param string $key Argument name. + * @param mixed $value Value. + * + * @return GenericEvent + */ + public function setArgument($key, $value) + { + $this->arguments[$key] = $value; + + return $this; + } + + /** + * Getter for all arguments. + * + * @return array + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Set args property. + * + * @param array $args Arguments. + * + * @return GenericEvent + */ + public function setArguments(array $args = array()) + { + $this->arguments = $args; + + return $this; + } + + /** + * Has argument. + * + * @param string $key Key of arguments array. + * + * @return boolean + */ + public function hasArgument($key) + { + return array_key_exists($key, $this->arguments); + } + + /** + * ArrayAccess for argument getter. + * + * @param string $key Array key. + * + * @throws \InvalidArgumentException If key does not exist in $this->args. + * + * @return mixed + */ + public function offsetGet($key) + { + return $this->getArgument($key); + } + + /** + * ArrayAccess for argument setter. + * + * @param string $key Array key to set. + * @param mixed $value Value. + */ + public function offsetSet($key, $value) + { + $this->setArgument($key, $value); + } + + /** + * ArrayAccess for unset argument. + * + * @param string $key Array key. + */ + public function offsetUnset($key) + { + if ($this->hasArgument($key)) { + unset($this->arguments[$key]); + } + } + + /** + * ArrayAccess has argument. + * + * @param string $key Array key. + * + * @return boolean + */ + public function offsetExists($key) + { + return $this->hasArgument($key); + } + + /** + * IteratorAggregate for iterating over the object like an array + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->arguments); + } +} diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php new file mode 100644 index 00000000..b70b81a8 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * A read-only proxy for an event dispatcher. + * + * @author Bernhard Schussek + */ +class ImmutableEventDispatcher implements EventDispatcherInterface +{ + /** + * The proxied dispatcher. + * @var EventDispatcherInterface + */ + private $dispatcher; + + /** + * Creates an unmodifiable proxy for an event dispatcher. + * + * @param EventDispatcherInterface $dispatcher The proxied event dispatcher. + */ + public function __construct(EventDispatcherInterface $dispatcher) + { + $this->dispatcher = $dispatcher; + } + + /** + * {@inheritdoc} + */ + public function dispatch($eventName, Event $event = null) + { + return $this->dispatcher->dispatch($eventName, $event); + } + + /** + * {@inheritdoc} + */ + public function addListener($eventName, $listener, $priority = 0) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function removeListener($eventName, $listener) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function removeSubscriber(EventSubscriberInterface $subscriber) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function getListeners($eventName = null) + { + return $this->dispatcher->getListeners($eventName); + } + + /** + * {@inheritdoc} + */ + public function hasListeners($eventName = null) + { + return $this->dispatcher->hasListeners($eventName); + } +} diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/LICENSE b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/LICENSE new file mode 100644 index 00000000..88a57f8d --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2013 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. diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/README.md b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/README.md new file mode 100644 index 00000000..11f6b188 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/README.md @@ -0,0 +1,25 @@ +EventDispatcher Component +========================= + +EventDispatcher implements a lightweight version of the Observer design +pattern. + + use Symfony\Component\EventDispatcher\EventDispatcher; + use Symfony\Component\EventDispatcher\Event; + + $dispatcher = new EventDispatcher(); + + $dispatcher->addListener('event_name', function (Event $event) { + // ... + }); + + $dispatcher->dispatch('event_name'); + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/EventDispatcher/ + $ composer.phar install --dev + $ phpunit diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php new file mode 100644 index 00000000..71f3ad05 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php @@ -0,0 +1,257 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\Scope; +use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +class ContainerAwareEventDispatcherTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\DependencyInjection\Container')) { + $this->markTestSkipped('The "DependencyInjection" component is not available'); + } + } + + public function testAddAListenerService() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $dispatcher->dispatch('onEvent', $event); + } + + public function testAddASubscriberService() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\SubscriberService'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container = new Container(); + $container->set('service.subscriber', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addSubscriberService('service.subscriber', 'Symfony\Component\EventDispatcher\Tests\SubscriberService'); + + $dispatcher->dispatch('onEvent', $event); + } + + public function testPreventDuplicateListenerService() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 5); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 10); + + $dispatcher->dispatch('onEvent', $event); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testTriggerAListenerServiceOutOfScope() + { + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $scope = new Scope('scope'); + $container = new Container(); + $container->addScope($scope); + $container->enterScope('scope'); + + $container->set('service.listener', $service, 'scope'); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $container->leaveScope('scope'); + $dispatcher->dispatch('onEvent'); + } + + public function testReEnteringAScope() + { + $event = new Event(); + + $service1 = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service1 + ->expects($this->exactly(2)) + ->method('onEvent') + ->with($event) + ; + + $scope = new Scope('scope'); + $container = new Container(); + $container->addScope($scope); + $container->enterScope('scope'); + + $container->set('service.listener', $service1, 'scope'); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + $dispatcher->dispatch('onEvent', $event); + + $service2 = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service2 + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container->enterScope('scope'); + $container->set('service.listener', $service2, 'scope'); + + $dispatcher->dispatch('onEvent', $event); + + $container->leaveScope('scope'); + + $dispatcher->dispatch('onEvent'); + } + + public function testHasListenersOnLazyLoad() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $event->setDispatcher($dispatcher); + $event->setName('onEvent'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $this->assertTrue($dispatcher->hasListeners()); + + if ($dispatcher->hasListeners('onEvent')) { + $dispatcher->dispatch('onEvent'); + } + } + + public function testGetListenersOnLazyLoad() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $listeners = $dispatcher->getListeners(); + + $this->assertTrue(isset($listeners['onEvent'])); + + $this->assertCount(1, $dispatcher->getListeners('onEvent')); + } + + public function testRemoveAfterDispatch() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $dispatcher->dispatch('onEvent', new Event()); + $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent')); + $this->assertFalse($dispatcher->hasListeners('onEvent')); + } + + public function testRemoveBeforeDispatch() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent')); + $this->assertFalse($dispatcher->hasListeners('onEvent')); + } +} + +class Service +{ + public function onEvent(Event $e) + { + } +} + +class SubscriberService implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array( + 'onEvent' => 'onEvent', + 'onEvent' => array('onEvent', 10), + 'onEvent' => array('onEvent'), + ); + } + + public function onEvent(Event $e) + { + } +} diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php new file mode 100644 index 00000000..ad7e4484 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php @@ -0,0 +1,320 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +class EventDispatcherTest extends \PHPUnit_Framework_TestCase +{ + /* Some pseudo events */ + const preFoo = 'pre.foo'; + const postFoo = 'post.foo'; + const preBar = 'pre.bar'; + const postBar = 'post.bar'; + + private $dispatcher; + + private $listener; + + protected function setUp() + { + $this->dispatcher = new EventDispatcher(); + $this->listener = new TestEventListener(); + } + + protected function tearDown() + { + $this->dispatcher = null; + $this->listener = null; + } + + public function testInitialState() + { + $this->assertEquals(array(), $this->dispatcher->getListeners()); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testAddListener() + { + $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + $this->assertCount(1, $this->dispatcher->getListeners(self::preFoo)); + $this->assertCount(1, $this->dispatcher->getListeners(self::postFoo)); + $this->assertCount(2, $this->dispatcher->getListeners()); + } + + public function testGetListenersSortsByPriority() + { + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); + $listener1->name = '1'; + $listener2->name = '2'; + $listener3->name = '3'; + + $this->dispatcher->addListener('pre.foo', array($listener1, 'preFoo'), -10); + $this->dispatcher->addListener('pre.foo', array($listener2, 'preFoo'), 10); + $this->dispatcher->addListener('pre.foo', array($listener3, 'preFoo')); + + $expected = array( + array($listener2, 'preFoo'), + array($listener3, 'preFoo'), + array($listener1, 'preFoo'), + ); + + $this->assertSame($expected, $this->dispatcher->getListeners('pre.foo')); + } + + public function testGetAllListenersSortsByPriority() + { + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); + $listener4 = new TestEventListener(); + $listener5 = new TestEventListener(); + $listener6 = new TestEventListener(); + + $this->dispatcher->addListener('pre.foo', $listener1, -10); + $this->dispatcher->addListener('pre.foo', $listener2); + $this->dispatcher->addListener('pre.foo', $listener3, 10); + $this->dispatcher->addListener('post.foo', $listener4, -10); + $this->dispatcher->addListener('post.foo', $listener5); + $this->dispatcher->addListener('post.foo', $listener6, 10); + + $expected = array( + 'pre.foo' => array($listener3, $listener2, $listener1), + 'post.foo' => array($listener6, $listener5, $listener4), + ); + + $this->assertSame($expected, $this->dispatcher->getListeners()); + } + + public function testDispatch() + { + $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); + $this->dispatcher->dispatch(self::preFoo); + $this->assertTrue($this->listener->preFooInvoked); + $this->assertFalse($this->listener->postFooInvoked); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch('noevent')); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(self::preFoo)); + $event = new Event(); + $return = $this->dispatcher->dispatch(self::preFoo, $event); + $this->assertEquals('pre.foo', $event->getName()); + $this->assertSame($event, $return); + } + + public function testDispatchForClosure() + { + $invoked = 0; + $listener = function () use (&$invoked) { + $invoked++; + }; + $this->dispatcher->addListener('pre.foo', $listener); + $this->dispatcher->addListener('post.foo', $listener); + $this->dispatcher->dispatch(self::preFoo); + $this->assertEquals(1, $invoked); + } + + public function testStopEventPropagation() + { + $otherListener = new TestEventListener(); + + // postFoo() stops the propagation, so only one listener should + // be executed + // Manually set priority to enforce $this->listener to be called first + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo'), 10); + $this->dispatcher->addListener('post.foo', array($otherListener, 'preFoo')); + $this->dispatcher->dispatch(self::postFoo); + $this->assertTrue($this->listener->postFooInvoked); + $this->assertFalse($otherListener->postFooInvoked); + } + + public function testDispatchByPriority() + { + $invoked = array(); + $listener1 = function () use (&$invoked) { + $invoked[] = '1'; + }; + $listener2 = function () use (&$invoked) { + $invoked[] = '2'; + }; + $listener3 = function () use (&$invoked) { + $invoked[] = '3'; + }; + $this->dispatcher->addListener('pre.foo', $listener1, -10); + $this->dispatcher->addListener('pre.foo', $listener2); + $this->dispatcher->addListener('pre.foo', $listener3, 10); + $this->dispatcher->dispatch(self::preFoo); + $this->assertEquals(array('3', '2', '1'), $invoked); + } + + public function testRemoveListener() + { + $this->dispatcher->addListener('pre.bar', $this->listener); + $this->assertTrue($this->dispatcher->hasListeners(self::preBar)); + $this->dispatcher->removeListener('pre.bar', $this->listener); + $this->assertFalse($this->dispatcher->hasListeners(self::preBar)); + $this->dispatcher->removeListener('notExists', $this->listener); + } + + public function testAddSubscriber() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testAddSubscriberWithPriorities() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $eventSubscriber = new TestEventSubscriberWithPriorities(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $listeners = $this->dispatcher->getListeners('pre.foo'); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $listeners); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Tests\TestEventSubscriberWithPriorities', $listeners[0][0]); + } + + public function testAddSubscriberWithMultipleListeners() + { + $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $listeners = $this->dispatcher->getListeners('pre.foo'); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $listeners); + $this->assertEquals('preFoo2', $listeners[0][1]); + } + + public function testRemoveSubscriber() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testRemoveSubscriberWithPriorities() + { + $eventSubscriber = new TestEventSubscriberWithPriorities(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + } + + public function testRemoveSubscriberWithMultipleListeners() + { + $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $this->dispatcher->getListeners(self::preFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + } + + public function testEventReceivesTheDispatcherInstance() + { + $test = $this; + $this->dispatcher->addListener('test', function ($event) use (&$dispatcher) { + $dispatcher = $event->getDispatcher(); + }); + $this->dispatcher->dispatch('test'); + $this->assertSame($this->dispatcher, $dispatcher); + } + + /** + * @see https://bugs.php.net/bug.php?id=62976 + * + * This bug affects: + * - The PHP 5.3 branch for versions < 5.3.18 + * - The PHP 5.4 branch for versions < 5.4.8 + * - The PHP 5.5 branch is not affected + */ + public function testWorkaroundForPhpBug62976() + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener('bug.62976', new CallableClass()); + $dispatcher->removeListener('bug.62976', function() {}); + $this->assertTrue($dispatcher->hasListeners('bug.62976')); + } +} + +class CallableClass +{ + public function __invoke() + { + } +} + +class TestEventListener +{ + public $preFooInvoked = false; + public $postFooInvoked = false; + + /* Listener methods */ + + public function preFoo(Event $e) + { + $this->preFooInvoked = true; + } + + public function postFoo(Event $e) + { + $this->postFooInvoked = true; + + $e->stopPropagation(); + } +} + +class TestEventSubscriber implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array('pre.foo' => 'preFoo', 'post.foo' => 'postFoo'); + } +} + +class TestEventSubscriberWithPriorities implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array( + 'pre.foo' => array('preFoo', 10), + 'post.foo' => array('postFoo'), + ); + } +} + +class TestEventSubscriberWithMultipleListeners implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array('pre.foo' => array( + array('preFoo1'), + array('preFoo2', 10) + )); + } +} diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventTest.php b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventTest.php new file mode 100644 index 00000000..52aa9ad6 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventTest.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventDispatcher; + +/** + * Test class for Event. + */ +class EventTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Symfony\Component\EventDispatcher\Event + */ + protected $event; + + /** + * @var \Symfony\Component\EventDispatcher\EventDispatcher + */ + protected $dispatcher; + + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + protected function setUp() + { + $this->event = new Event; + $this->dispatcher = new EventDispatcher(); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + $this->event = null; + $this->eventDispatcher = null; + } + + public function testIsPropagationStopped() + { + $this->assertFalse($this->event->isPropagationStopped()); + } + + public function testStopPropagationAndIsPropagationStopped() + { + $this->event->stopPropagation(); + $this->assertTrue($this->event->isPropagationStopped()); + } + + public function testSetDispatcher() + { + $this->event->setDispatcher($this->dispatcher); + $this->assertSame($this->dispatcher, $this->event->getDispatcher()); + } + + public function testGetDispatcher() + { + $this->assertNull($this->event->getDispatcher()); + } + + public function testGetName() + { + $this->assertNull($this->event->getName()); + } + + public function testSetName() + { + $this->event->setName('foo'); + $this->assertEquals('foo', $this->event->getName()); + } +} diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php new file mode 100644 index 00000000..8dd6f5b4 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php @@ -0,0 +1,140 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\GenericEvent; + +/** + * Test class for Event. + */ +class GenericEventTest extends \PHPUnit_Framework_TestCase +{ + + /** + * @var GenericEvent + */ + private $event; + + private $subject; + + /** + * Prepares the environment before running a test. + */ + protected function setUp() + { + parent::setUp(); + + $this->subject = new \StdClass(); + $this->event = new GenericEvent($this->subject, array('name' => 'Event'), 'foo'); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->subject = null; + $this->event = null; + + parent::tearDown(); + } + + public function testConstruct() + { + $this->assertEquals($this->event, new GenericEvent($this->subject, array('name' => 'Event'))); + } + + /** + * Tests Event->getArgs() + */ + public function testGetArguments() + { + // test getting all + $this->assertSame(array('name' => 'Event'), $this->event->getArguments()); + } + + public function testSetArguments() + { + $result = $this->event->setArguments(array('foo' => 'bar')); + $this->assertAttributeSame(array('foo' => 'bar'), 'arguments', $this->event); + $this->assertSame($this->event, $result); + } + + public function testSetArgument() + { + $result = $this->event->setArgument('foo2', 'bar2'); + $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event); + $this->assertEquals($this->event, $result); + } + + public function testGetArgument() + { + // test getting key + $this->assertEquals('Event', $this->event->getArgument('name')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testGetArgException() + { + $this->event->getArgument('nameNotExist'); + } + + public function testOffsetGet() + { + // test getting key + $this->assertEquals('Event', $this->event['name']); + + // test getting invalid arg + $this->setExpectedException('InvalidArgumentException'); + $this->assertFalse($this->event['nameNotExist']); + } + + public function testOffsetSet() + { + $this->event['foo2'] = 'bar2'; + $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event); + } + + public function testOffsetUnset() + { + unset($this->event['name']); + $this->assertAttributeSame(array(), 'arguments', $this->event); + } + + public function testOffsetIsset() + { + $this->assertTrue(isset($this->event['name'])); + $this->assertFalse(isset($this->event['nameNotExist'])); + } + + public function testHasArgument() + { + $this->assertTrue($this->event->hasArgument('name')); + $this->assertFalse($this->event->hasArgument('nameNotExist')); + } + + public function testGetSubject() + { + $this->assertSame($this->subject, $this->event->getSubject()); + } + + public function testHasIterator() + { + $data = array(); + foreach ($this->event as $key => $value) { + $data[$key] = $value; + } + $this->assertEquals(array('name' => 'Event'), $data); + } +} diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php new file mode 100644 index 00000000..6402f89f --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\ImmutableEventDispatcher; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * @author Bernhard Schussek + */ +class ImmutableEventDispatcherTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $innerDispatcher; + + /** + * @var ImmutableEventDispatcher + */ + private $dispatcher; + + protected function setUp() + { + $this->innerDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->dispatcher = new ImmutableEventDispatcher($this->innerDispatcher); + } + + public function testDispatchDelegates() + { + $event = new Event(); + + $this->innerDispatcher->expects($this->once()) + ->method('dispatch') + ->with('event', $event) + ->will($this->returnValue('result')); + + $this->assertSame('result', $this->dispatcher->dispatch('event', $event)); + } + + public function testGetListenersDelegates() + { + $this->innerDispatcher->expects($this->once()) + ->method('getListeners') + ->with('event') + ->will($this->returnValue('result')); + + $this->assertSame('result', $this->dispatcher->getListeners('event')); + } + + public function testHasListenersDelegates() + { + $this->innerDispatcher->expects($this->once()) + ->method('hasListeners') + ->with('event') + ->will($this->returnValue('result')); + + $this->assertSame('result', $this->dispatcher->hasListeners('event')); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testAddListenerDisallowed() + { + $this->dispatcher->addListener('event', function () { return 'foo'; }); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testAddSubscriberDisallowed() + { + $subscriber = $this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface'); + + $this->dispatcher->addSubscriber($subscriber); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testRemoveListenerDisallowed() + { + $this->dispatcher->removeListener('event', function () { return 'foo'; }); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testRemoveSubscriberDisallowed() + { + $subscriber = $this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface'); + + $this->dispatcher->removeSubscriber($subscriber); + } +} diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/composer.json b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/composer.json new file mode 100644 index 00000000..1db2ecfd --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/composer.json @@ -0,0 +1,38 @@ +{ + "name": "symfony/event-dispatcher", + "type": "library", + "description": "Symfony EventDispatcher Component", + "keywords": [], + "homepage": "http://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/dependency-injection": "~2.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\EventDispatcher\\": "" } + }, + "target-dir": "Symfony/Component/EventDispatcher", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + } +} diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/phpunit.xml.dist b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/phpunit.xml.dist new file mode 100644 index 00000000..0c3de4f7 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/phpunit.xml.dist @@ -0,0 +1,30 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/filesystem/Symfony/Component/Filesystem/.gitignore b/vendor/symfony/filesystem/Symfony/Component/Filesystem/.gitignore new file mode 100644 index 00000000..44de97a3 --- /dev/null +++ b/vendor/symfony/filesystem/Symfony/Component/Filesystem/.gitignore @@ -0,0 +1,4 @@ +vendor/ +composer.lock +phpunit.xml + diff --git a/vendor/symfony/filesystem/Symfony/Component/Filesystem/CHANGELOG.md b/vendor/symfony/filesystem/Symfony/Component/Filesystem/CHANGELOG.md new file mode 100644 index 00000000..e6aee66a --- /dev/null +++ b/vendor/symfony/filesystem/Symfony/Component/Filesystem/CHANGELOG.md @@ -0,0 +1,18 @@ +CHANGELOG +========= + +2.3.0 +----- + + * added the dumpFile() method to atomically write files + +2.2.0 +----- + + * added a delete option for the mirror() method + +2.1.0 +----- + + * 24eb396 : BC Break : mkdir() function now throws exception in case of failure instead of returning Boolean value + * created the component diff --git a/vendor/symfony/filesystem/Symfony/Component/Filesystem/Exception/ExceptionInterface.php b/vendor/symfony/filesystem/Symfony/Component/Filesystem/Exception/ExceptionInterface.php new file mode 100644 index 00000000..bc9748d7 --- /dev/null +++ b/vendor/symfony/filesystem/Symfony/Component/Filesystem/Exception/ExceptionInterface.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Exception; + +/** + * Exception interface for all exceptions thrown by the component. + * + * @author Romain Neutron + * + * @api + */ +interface ExceptionInterface +{ + +} diff --git a/vendor/symfony/filesystem/Symfony/Component/Filesystem/Exception/IOException.php b/vendor/symfony/filesystem/Symfony/Component/Filesystem/Exception/IOException.php new file mode 100644 index 00000000..5b27e661 --- /dev/null +++ b/vendor/symfony/filesystem/Symfony/Component/Filesystem/Exception/IOException.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Exception; + +/** + * Exception class thrown when a filesystem operation failure happens + * + * @author Romain Neutron + * + * @api + */ +class IOException extends \RuntimeException implements ExceptionInterface +{ + +} diff --git a/vendor/symfony/filesystem/Symfony/Component/Filesystem/Filesystem.php b/vendor/symfony/filesystem/Symfony/Component/Filesystem/Filesystem.php new file mode 100644 index 00000000..6e015b4b --- /dev/null +++ b/vendor/symfony/filesystem/Symfony/Component/Filesystem/Filesystem.php @@ -0,0 +1,471 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem; + +use Symfony\Component\Filesystem\Exception\IOException; + +/** + * Provides basic utility to manipulate the file system. + * + * @author Fabien Potencier + */ +class Filesystem +{ + /** + * Copies a file. + * + * This method only copies the file if the origin file is newer than the target file. + * + * By default, if the target already exists, it is not overridden. + * + * @param string $originFile The original filename + * @param string $targetFile The target filename + * @param boolean $override Whether to override an existing file or not + * + * @throws IOException When copy fails + */ + public function copy($originFile, $targetFile, $override = false) + { + if (stream_is_local($originFile) && !is_file($originFile)) { + throw new IOException(sprintf('Failed to copy %s because file not exists', $originFile)); + } + + $this->mkdir(dirname($targetFile)); + + if (!$override && is_file($targetFile)) { + $doCopy = filemtime($originFile) > filemtime($targetFile); + } else { + $doCopy = true; + } + + if ($doCopy) { + // https://bugs.php.net/bug.php?id=64634 + $source = fopen($originFile, 'r'); + $target = fopen($targetFile, 'w+'); + stream_copy_to_stream($source, $target); + fclose($source); + fclose($target); + unset($source, $target); + + if (!is_file($targetFile)) { + throw new IOException(sprintf('Failed to copy %s to %s', $originFile, $targetFile)); + } + } + } + + /** + * Creates a directory recursively. + * + * @param string|array|\Traversable $dirs The directory path + * @param integer $mode The directory mode + * + * @throws IOException On any directory creation failure + */ + public function mkdir($dirs, $mode = 0777) + { + foreach ($this->toIterator($dirs) as $dir) { + if (is_dir($dir)) { + continue; + } + + if (true !== @mkdir($dir, $mode, true)) { + throw new IOException(sprintf('Failed to create %s', $dir)); + } + } + } + + /** + * Checks the existence of files or directories. + * + * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to check + * + * @return Boolean true if the file exists, false otherwise + */ + public function exists($files) + { + foreach ($this->toIterator($files) as $file) { + if (!file_exists($file)) { + return false; + } + } + + return true; + } + + /** + * Sets access and modification time of file. + * + * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to create + * @param integer $time The touch time as a unix timestamp + * @param integer $atime The access time as a unix timestamp + * + * @throws IOException When touch fails + */ + public function touch($files, $time = null, $atime = null) + { + foreach ($this->toIterator($files) as $file) { + $touch = $time ? @touch($file, $time, $atime) : @touch($file); + if (true !== $touch) { + throw new IOException(sprintf('Failed to touch %s', $file)); + } + } + } + + /** + * Removes files or directories. + * + * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to remove + * + * @throws IOException When removal fails + */ + public function remove($files) + { + $files = iterator_to_array($this->toIterator($files)); + $files = array_reverse($files); + foreach ($files as $file) { + if (!file_exists($file) && !is_link($file)) { + continue; + } + + if (is_dir($file) && !is_link($file)) { + $this->remove(new \FilesystemIterator($file)); + + if (true !== @rmdir($file)) { + throw new IOException(sprintf('Failed to remove directory %s', $file)); + } + } else { + // https://bugs.php.net/bug.php?id=52176 + if (defined('PHP_WINDOWS_VERSION_MAJOR') && is_dir($file)) { + if (true !== @rmdir($file)) { + throw new IOException(sprintf('Failed to remove file %s', $file)); + } + } else { + if (true !== @unlink($file)) { + throw new IOException(sprintf('Failed to remove file %s', $file)); + } + } + } + } + } + + /** + * Change mode for an array of files or directories. + * + * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change mode + * @param integer $mode The new mode (octal) + * @param integer $umask The mode mask (octal) + * @param Boolean $recursive Whether change the mod recursively or not + * + * @throws IOException When the change fail + */ + public function chmod($files, $mode, $umask = 0000, $recursive = false) + { + foreach ($this->toIterator($files) as $file) { + if ($recursive && is_dir($file) && !is_link($file)) { + $this->chmod(new \FilesystemIterator($file), $mode, $umask, true); + } + if (true !== @chmod($file, $mode & ~$umask)) { + throw new IOException(sprintf('Failed to chmod file %s', $file)); + } + } + } + + /** + * Change the owner of an array of files or directories + * + * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change owner + * @param string $user The new owner user name + * @param Boolean $recursive Whether change the owner recursively or not + * + * @throws IOException When the change fail + */ + public function chown($files, $user, $recursive = false) + { + foreach ($this->toIterator($files) as $file) { + if ($recursive && is_dir($file) && !is_link($file)) { + $this->chown(new \FilesystemIterator($file), $user, true); + } + if (is_link($file) && function_exists('lchown')) { + if (true !== @lchown($file, $user)) { + throw new IOException(sprintf('Failed to chown file %s', $file)); + } + } else { + if (true !== @chown($file, $user)) { + throw new IOException(sprintf('Failed to chown file %s', $file)); + } + } + } + } + + /** + * Change the group of an array of files or directories + * + * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change group + * @param string $group The group name + * @param Boolean $recursive Whether change the group recursively or not + * + * @throws IOException When the change fail + */ + public function chgrp($files, $group, $recursive = false) + { + foreach ($this->toIterator($files) as $file) { + if ($recursive && is_dir($file) && !is_link($file)) { + $this->chgrp(new \FilesystemIterator($file), $group, true); + } + if (is_link($file) && function_exists('lchgrp')) { + if (true !== @lchgrp($file, $group)) { + throw new IOException(sprintf('Failed to chgrp file %s', $file)); + } + } else { + if (true !== @chgrp($file, $group)) { + throw new IOException(sprintf('Failed to chgrp file %s', $file)); + } + } + } + } + + /** + * Renames a file or a directory. + * + * @param string $origin The origin filename or directory + * @param string $target The new filename or directory + * @param Boolean $overwrite Whether to overwrite the target if it already exists + * + * @throws IOException When target file or directory already exists + * @throws IOException When origin cannot be renamed + */ + public function rename($origin, $target, $overwrite = false) + { + // we check that target does not exist + if (!$overwrite && is_readable($target)) { + throw new IOException(sprintf('Cannot rename because the target "%s" already exist.', $target)); + } + + if (true !== @rename($origin, $target)) { + throw new IOException(sprintf('Cannot rename "%s" to "%s".', $origin, $target)); + } + } + + /** + * Creates a symbolic link or copy a directory. + * + * @param string $originDir The origin directory path + * @param string $targetDir The symbolic link name + * @param Boolean $copyOnWindows Whether to copy files if on Windows + * + * @throws IOException When symlink fails + */ + public function symlink($originDir, $targetDir, $copyOnWindows = false) + { + if (!function_exists('symlink') && $copyOnWindows) { + $this->mirror($originDir, $targetDir); + + return; + } + + $this->mkdir(dirname($targetDir)); + + $ok = false; + if (is_link($targetDir)) { + if (readlink($targetDir) != $originDir) { + $this->remove($targetDir); + } else { + $ok = true; + } + } + + if (!$ok) { + if (true !== @symlink($originDir, $targetDir)) { + $report = error_get_last(); + if (is_array($report)) { + if (defined('PHP_WINDOWS_VERSION_MAJOR') && false !== strpos($report['message'], 'error code(1314)')) { + throw new IOException('Unable to create symlink due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?'); + } + } + throw new IOException(sprintf('Failed to create symbolic link from %s to %s', $originDir, $targetDir)); + } + } + } + + /** + * Given an existing path, convert it to a path relative to a given starting path + * + * @param string $endPath Absolute path of target + * @param string $startPath Absolute path where traversal begins + * + * @return string Path of target relative to starting path + */ + public function makePathRelative($endPath, $startPath) + { + // Normalize separators on windows + if (defined('PHP_WINDOWS_VERSION_MAJOR')) { + $endPath = strtr($endPath, '\\', '/'); + $startPath = strtr($startPath, '\\', '/'); + } + + // Split the paths into arrays + $startPathArr = explode('/', trim($startPath, '/')); + $endPathArr = explode('/', trim($endPath, '/')); + + // Find for which directory the common path stops + $index = 0; + while (isset($startPathArr[$index]) && isset($endPathArr[$index]) && $startPathArr[$index] === $endPathArr[$index]) { + $index++; + } + + // Determine how deep the start path is relative to the common path (ie, "web/bundles" = 2 levels) + $depth = count($startPathArr) - $index; + + // Repeated "../" for each level need to reach the common path + $traverser = str_repeat('../', $depth); + + $endPathRemainder = implode('/', array_slice($endPathArr, $index)); + + // Construct $endPath from traversing to the common path, then to the remaining $endPath + $relativePath = $traverser.(strlen($endPathRemainder) > 0 ? $endPathRemainder.'/' : ''); + + return (strlen($relativePath) === 0) ? './' : $relativePath; + } + + /** + * Mirrors a directory to another. + * + * @param string $originDir The origin directory + * @param string $targetDir The target directory + * @param \Traversable $iterator A Traversable instance + * @param array $options An array of boolean options + * Valid options are: + * - $options['override'] Whether to override an existing file on copy or not (see copy()) + * - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink()) + * - $options['delete'] Whether to delete files that are not in the source directory (defaults to false) + * + * @throws IOException When file type is unknown + */ + public function mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array()) + { + $targetDir = rtrim($targetDir, '/\\'); + $originDir = rtrim($originDir, '/\\'); + + // Iterate in destination folder to remove obsolete entries + if ($this->exists($targetDir) && isset($options['delete']) && $options['delete']) { + $deleteIterator = $iterator; + if (null === $deleteIterator) { + $flags = \FilesystemIterator::SKIP_DOTS; + $deleteIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($targetDir, $flags), \RecursiveIteratorIterator::CHILD_FIRST); + } + foreach ($deleteIterator as $file) { + $origin = str_replace($targetDir, $originDir, $file->getPathname()); + if (!$this->exists($origin)) { + $this->remove($file); + } + } + } + + $copyOnWindows = false; + if (isset($options['copy_on_windows']) && !function_exists('symlink')) { + $copyOnWindows = $options['copy_on_windows']; + } + + if (null === $iterator) { + $flags = $copyOnWindows ? \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS : \FilesystemIterator::SKIP_DOTS; + $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($originDir, $flags), \RecursiveIteratorIterator::SELF_FIRST); + } + + foreach ($iterator as $file) { + $target = str_replace($originDir, $targetDir, $file->getPathname()); + + if ($copyOnWindows) { + if (is_link($file) || is_file($file)) { + $this->copy($file, $target, isset($options['override']) ? $options['override'] : false); + } elseif (is_dir($file)) { + $this->mkdir($target); + } else { + throw new IOException(sprintf('Unable to guess "%s" file type.', $file)); + } + } else { + if (is_link($file)) { + $this->symlink($file, $target); + } elseif (is_dir($file)) { + $this->mkdir($target); + } elseif (is_file($file)) { + $this->copy($file, $target, isset($options['override']) ? $options['override'] : false); + } else { + throw new IOException(sprintf('Unable to guess "%s" file type.', $file)); + } + } + } + } + + /** + * Returns whether the file path is an absolute path. + * + * @param string $file A file path + * + * @return Boolean + */ + public function isAbsolutePath($file) + { + if (strspn($file, '/\\', 0, 1) + || (strlen($file) > 3 && ctype_alpha($file[0]) + && substr($file, 1, 1) === ':' + && (strspn($file, '/\\', 2, 1)) + ) + || null !== parse_url($file, PHP_URL_SCHEME) + ) { + return true; + } + + return false; + } + + /** + * @param mixed $files + * + * @return \Traversable + */ + private function toIterator($files) + { + if (!$files instanceof \Traversable) { + $files = new \ArrayObject(is_array($files) ? $files : array($files)); + } + + return $files; + } + + /** + * Atomically dumps content into a file. + * + * @param string $filename The file to be written to. + * @param string $content The data to write into the file. + * @param integer $mode The file mode (octal). + * @throws IOException If the file cannot be written to. + */ + public function dumpFile($filename, $content, $mode = 0666) + { + $dir = dirname($filename); + + if (!is_dir($dir)) { + $this->mkdir($dir); + } elseif (!is_writable($dir)) { + throw new IOException(sprintf('Unable to write in the %s directory\n', $dir)); + } + + $tmpFile = tempnam($dir, basename($filename)); + + if (false === @file_put_contents($tmpFile, $content)) { + throw new IOException(sprintf('Failed to write file "%s".', $filename)); + } + + $this->rename($tmpFile, $filename, true); + $this->chmod($filename, $mode); + } +} diff --git a/vendor/symfony/filesystem/Symfony/Component/Filesystem/LICENSE b/vendor/symfony/filesystem/Symfony/Component/Filesystem/LICENSE new file mode 100644 index 00000000..88a57f8d --- /dev/null +++ b/vendor/symfony/filesystem/Symfony/Component/Filesystem/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2013 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. diff --git a/vendor/symfony/filesystem/Symfony/Component/Filesystem/README.md b/vendor/symfony/filesystem/Symfony/Component/Filesystem/README.md new file mode 100644 index 00000000..94ac1469 --- /dev/null +++ b/vendor/symfony/filesystem/Symfony/Component/Filesystem/README.md @@ -0,0 +1,45 @@ +Filesystem Component +==================== + +Filesystem provides basic utility to manipulate the file system: + +```php +copy($originFile, $targetFile, $override = false); + +$filesystem->mkdir($dirs, $mode = 0777); + +$filesystem->touch($files, $time = null, $atime = null); + +$filesystem->remove($files); + +$filesystem->chmod($files, $mode, $umask = 0000, $recursive = false); + +$filesystem->chown($files, $user, $recursive = false); + +$filesystem->chgrp($files, $group, $recursive = false); + +$filesystem->rename($origin, $target); + +$filesystem->symlink($originDir, $targetDir, $copyOnWindows = false); + +$filesystem->makePathRelative($endPath, $startPath); + +$filesystem->mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array()); + +$filesystem->isAbsolutePath($file); +``` + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/Filesystem/ + $ composer.phar install --dev + $ phpunit diff --git a/vendor/symfony/filesystem/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/vendor/symfony/filesystem/Symfony/Component/Filesystem/Tests/FilesystemTest.php new file mode 100644 index 00000000..02969f30 --- /dev/null +++ b/vendor/symfony/filesystem/Symfony/Component/Filesystem/Tests/FilesystemTest.php @@ -0,0 +1,982 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Tests; + +use Symfony\Component\Filesystem\Filesystem; + +/** + * Test class for Filesystem. + */ +class FilesystemTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var string $workspace + */ + private $workspace = null; + + /** + * @var \Symfony\Component\Filesystem\Filesystem $filesystem + */ + private $filesystem = null; + + private static $symlinkOnWindows = null; + + public static function setUpBeforeClass() + { + if (defined('PHP_WINDOWS_VERSION_MAJOR')) { + self::$symlinkOnWindows = true; + $originDir = tempnam(sys_get_temp_dir(), 'sl'); + $targetDir = tempnam(sys_get_temp_dir(), 'sl'); + if (true !== @symlink($originDir, $targetDir)) { + $report = error_get_last(); + if (is_array($report) && false !== strpos($report['message'], 'error code(1314)')) { + self::$symlinkOnWindows = false; + } + } + } + } + + public function setUp() + { + $this->filesystem = new Filesystem(); + $this->workspace = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.time().rand(0, 1000); + mkdir($this->workspace, 0777, true); + $this->workspace = realpath($this->workspace); + } + + public function tearDown() + { + $this->clean($this->workspace); + } + + /** + * @param string $file + */ + private function clean($file) + { + if (is_dir($file) && !is_link($file)) { + $dir = new \FilesystemIterator($file); + foreach ($dir as $childFile) { + $this->clean($childFile); + } + + rmdir($file); + } else { + unlink($file); + } + } + + public function testCopyCreatesNewFile() + { + $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; + + file_put_contents($sourceFilePath, 'SOURCE FILE'); + + $this->filesystem->copy($sourceFilePath, $targetFilePath); + + $this->assertFileExists($targetFilePath); + $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath)); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testCopyFails() + { + $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; + + $this->filesystem->copy($sourceFilePath, $targetFilePath); + } + + public function testCopyOverridesExistingFileIfModified() + { + $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; + + file_put_contents($sourceFilePath, 'SOURCE FILE'); + file_put_contents($targetFilePath, 'TARGET FILE'); + touch($targetFilePath, time() - 1000); + + $this->filesystem->copy($sourceFilePath, $targetFilePath); + + $this->assertFileExists($targetFilePath); + $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath)); + } + + public function testCopyDoesNotOverrideExistingFileByDefault() + { + $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; + + file_put_contents($sourceFilePath, 'SOURCE FILE'); + file_put_contents($targetFilePath, 'TARGET FILE'); + + // make sure both files have the same modification time + $modificationTime = time() - 1000; + touch($sourceFilePath, $modificationTime); + touch($targetFilePath, $modificationTime); + + $this->filesystem->copy($sourceFilePath, $targetFilePath); + + $this->assertFileExists($targetFilePath); + $this->assertEquals('TARGET FILE', file_get_contents($targetFilePath)); + } + + public function testCopyOverridesExistingFileIfForced() + { + $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; + + file_put_contents($sourceFilePath, 'SOURCE FILE'); + file_put_contents($targetFilePath, 'TARGET FILE'); + + // make sure both files have the same modification time + $modificationTime = time() - 1000; + touch($sourceFilePath, $modificationTime); + touch($targetFilePath, $modificationTime); + + $this->filesystem->copy($sourceFilePath, $targetFilePath, true); + + $this->assertFileExists($targetFilePath); + $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath)); + } + + public function testCopyCreatesTargetDirectoryIfItDoesNotExist() + { + $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFileDirectory = $this->workspace.DIRECTORY_SEPARATOR.'directory'; + $targetFilePath = $targetFileDirectory.DIRECTORY_SEPARATOR.'copy_target_file'; + + file_put_contents($sourceFilePath, 'SOURCE FILE'); + + $this->filesystem->copy($sourceFilePath, $targetFilePath); + + $this->assertTrue(is_dir($targetFileDirectory)); + $this->assertFileExists($targetFilePath); + $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath)); + } + + public function testMkdirCreatesDirectoriesRecursively() + { + $directory = $this->workspace + .DIRECTORY_SEPARATOR.'directory' + .DIRECTORY_SEPARATOR.'sub_directory'; + + $this->filesystem->mkdir($directory); + + $this->assertTrue(is_dir($directory)); + } + + public function testMkdirCreatesDirectoriesFromArray() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + $directories = array( + $basePath.'1', $basePath.'2', $basePath.'3' + ); + + $this->filesystem->mkdir($directories); + + $this->assertTrue(is_dir($basePath.'1')); + $this->assertTrue(is_dir($basePath.'2')); + $this->assertTrue(is_dir($basePath.'3')); + } + + public function testMkdirCreatesDirectoriesFromTraversableObject() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + $directories = new \ArrayObject(array( + $basePath.'1', $basePath.'2', $basePath.'3' + )); + + $this->filesystem->mkdir($directories); + + $this->assertTrue(is_dir($basePath.'1')); + $this->assertTrue(is_dir($basePath.'2')); + $this->assertTrue(is_dir($basePath.'3')); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testMkdirCreatesDirectoriesFails() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + $dir = $basePath.'2'; + + file_put_contents($dir, ''); + + $this->filesystem->mkdir($dir); + } + + public function testTouchCreatesEmptyFile() + { + $file = $this->workspace.DIRECTORY_SEPARATOR.'1'; + + $this->filesystem->touch($file); + + $this->assertFileExists($file); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testTouchFails() + { + $file = $this->workspace.DIRECTORY_SEPARATOR.'1'.DIRECTORY_SEPARATOR.'2'; + + $this->filesystem->touch($file); + } + + public function testTouchCreatesEmptyFilesFromArray() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + $files = array( + $basePath.'1', $basePath.'2', $basePath.'3' + ); + + $this->filesystem->touch($files); + + $this->assertFileExists($basePath.'1'); + $this->assertFileExists($basePath.'2'); + $this->assertFileExists($basePath.'3'); + } + + public function testTouchCreatesEmptyFilesFromTraversableObject() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + $files = new \ArrayObject(array( + $basePath.'1', $basePath.'2', $basePath.'3' + )); + + $this->filesystem->touch($files); + + $this->assertFileExists($basePath.'1'); + $this->assertFileExists($basePath.'2'); + $this->assertFileExists($basePath.'3'); + } + + public function testRemoveCleansFilesAndDirectoriesIteratively() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR; + + mkdir($basePath); + mkdir($basePath.'dir'); + touch($basePath.'file'); + + $this->filesystem->remove($basePath); + + $this->assertTrue(!is_dir($basePath)); + } + + public function testRemoveCleansArrayOfFilesAndDirectories() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + + mkdir($basePath.'dir'); + touch($basePath.'file'); + + $files = array( + $basePath.'dir', $basePath.'file' + ); + + $this->filesystem->remove($files); + + $this->assertTrue(!is_dir($basePath.'dir')); + $this->assertTrue(!is_file($basePath.'file')); + } + + public function testRemoveCleansTraversableObjectOfFilesAndDirectories() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + + mkdir($basePath.'dir'); + touch($basePath.'file'); + + $files = new \ArrayObject(array( + $basePath.'dir', $basePath.'file' + )); + + $this->filesystem->remove($files); + + $this->assertTrue(!is_dir($basePath.'dir')); + $this->assertTrue(!is_file($basePath.'file')); + } + + public function testRemoveIgnoresNonExistingFiles() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + + mkdir($basePath.'dir'); + + $files = array( + $basePath.'dir', $basePath.'file' + ); + + $this->filesystem->remove($files); + + $this->assertTrue(!is_dir($basePath.'dir')); + } + + public function testRemoveCleansInvalidLinks() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR; + + mkdir($basePath); + mkdir($basePath.'dir'); + // create symlink to unexisting file + @symlink($basePath.'file', $basePath.'link'); + + $this->filesystem->remove($basePath); + + $this->assertTrue(!is_dir($basePath)); + } + + public function testFilesExists() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR; + + mkdir($basePath); + touch($basePath.'file1'); + mkdir($basePath.'folder'); + + $this->assertTrue($this->filesystem->exists($basePath.'file1')); + $this->assertTrue($this->filesystem->exists($basePath.'folder')); + } + + public function testFilesExistsTraversableObjectOfFilesAndDirectories() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + + mkdir($basePath.'dir'); + touch($basePath.'file'); + + $files = new \ArrayObject(array( + $basePath.'dir', $basePath.'file' + )); + + $this->assertTrue($this->filesystem->exists($files)); + } + + public function testFilesNotExistsTraversableObjectOfFilesAndDirectories() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + + mkdir($basePath.'dir'); + touch($basePath.'file'); + touch($basePath.'file2'); + + $files = new \ArrayObject(array( + $basePath.'dir', $basePath.'file', $basePath.'file2' + )); + + unlink($basePath.'file'); + + $this->assertFalse($this->filesystem->exists($files)); + } + + public function testInvalidFileNotExists() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR; + + $this->assertFalse($this->filesystem->exists($basePath.time())); + } + + public function testChmodChangesFileMode() + { + $this->markAsSkippedIfChmodIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + $file = $dir.DIRECTORY_SEPARATOR.'file'; + touch($file); + + $this->filesystem->chmod($file, 0400); + $this->filesystem->chmod($dir, 0753); + + $this->assertEquals(753, $this->getFilePermissions($dir)); + $this->assertEquals(400, $this->getFilePermissions($file)); + } + + public function testChmodWrongMod() + { + $this->markAsSkippedIfChmodIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'file'; + touch($dir); + + $this->filesystem->chmod($dir, 'Wrongmode'); + } + + public function testChmodRecursive() + { + $this->markAsSkippedIfChmodIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + $file = $dir.DIRECTORY_SEPARATOR.'file'; + touch($file); + + $this->filesystem->chmod($file, 0400, 0000, true); + $this->filesystem->chmod($dir, 0753, 0000, true); + + $this->assertEquals(753, $this->getFilePermissions($dir)); + $this->assertEquals(753, $this->getFilePermissions($file)); + } + + public function testChmodAppliesUmask() + { + $this->markAsSkippedIfChmodIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + touch($file); + + $this->filesystem->chmod($file, 0770, 0022); + $this->assertEquals(750, $this->getFilePermissions($file)); + } + + public function testChmodChangesModeOfArrayOfFiles() + { + $this->markAsSkippedIfChmodIsMissing(); + + $directory = $this->workspace.DIRECTORY_SEPARATOR.'directory'; + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $files = array($directory, $file); + + mkdir($directory); + touch($file); + + $this->filesystem->chmod($files, 0753); + + $this->assertEquals(753, $this->getFilePermissions($file)); + $this->assertEquals(753, $this->getFilePermissions($directory)); + } + + public function testChmodChangesModeOfTraversableFileObject() + { + $this->markAsSkippedIfChmodIsMissing(); + + $directory = $this->workspace.DIRECTORY_SEPARATOR.'directory'; + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $files = new \ArrayObject(array($directory, $file)); + + mkdir($directory); + touch($file); + + $this->filesystem->chmod($files, 0753); + + $this->assertEquals(753, $this->getFilePermissions($file)); + $this->assertEquals(753, $this->getFilePermissions($directory)); + } + + public function testChown() + { + $this->markAsSkippedIfPosixIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + + $this->filesystem->chown($dir, $this->getFileOwner($dir)); + } + + public function testChownRecursive() + { + $this->markAsSkippedIfPosixIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + $file = $dir.DIRECTORY_SEPARATOR.'file'; + touch($file); + + $this->filesystem->chown($dir, $this->getFileOwner($dir), true); + } + + public function testChownSymlink() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->symlink($file, $link); + + $this->filesystem->chown($link, $this->getFileOwner($link)); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testChownSymlinkFails() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->symlink($file, $link); + + $this->filesystem->chown($link, 'user'.time().mt_rand(1000, 9999)); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testChownFail() + { + $this->markAsSkippedIfPosixIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + + $this->filesystem->chown($dir, 'user'.time().mt_rand(1000, 9999)); + } + + public function testChgrp() + { + $this->markAsSkippedIfPosixIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + + $this->filesystem->chgrp($dir, $this->getFileGroup($dir)); + } + + public function testChgrpRecursive() + { + $this->markAsSkippedIfPosixIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + $file = $dir.DIRECTORY_SEPARATOR.'file'; + touch($file); + + $this->filesystem->chgrp($dir, $this->getFileGroup($dir), true); + } + + public function testChgrpSymlink() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->symlink($file, $link); + + $this->filesystem->chgrp($link, $this->getFileGroup($link)); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testChgrpSymlinkFails() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->symlink($file, $link); + + $this->filesystem->chgrp($link, 'user'.time().mt_rand(1000, 9999)); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testChgrpFail() + { + $this->markAsSkippedIfPosixIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + + $this->filesystem->chgrp($dir, 'user'.time().mt_rand(1000, 9999)); + } + + public function testRename() + { + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file'; + touch($file); + + $this->filesystem->rename($file, $newPath); + + $this->assertFileNotExists($file); + $this->assertFileExists($newPath); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testRenameThrowsExceptionIfTargetAlreadyExists() + { + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file'; + + touch($file); + touch($newPath); + + $this->filesystem->rename($file, $newPath); + } + + public function testRenameOverwritesTheTargetIfItAlreadyExists() + { + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file'; + + touch($file); + touch($newPath); + + $this->filesystem->rename($file, $newPath, true); + + $this->assertFileNotExists($file); + $this->assertFileExists($newPath); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testRenameThrowsExceptionOnError() + { + $file = $this->workspace.DIRECTORY_SEPARATOR.uniqid(); + $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file'; + + $this->filesystem->rename($file, $newPath); + } + + public function testSymlink() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->symlink($file, $link); + + $this->assertTrue(is_link($link)); + $this->assertEquals($file, readlink($link)); + } + + /** + * @depends testSymlink + */ + public function testRemoveSymlink() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; + + $this->filesystem->remove($link); + + $this->assertTrue(!is_link($link)); + } + + public function testSymlinkIsOverwrittenIfPointsToDifferentTarget() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; + + touch($file); + symlink($this->workspace, $link); + + $this->filesystem->symlink($file, $link); + + $this->assertTrue(is_link($link)); + $this->assertEquals($file, readlink($link)); + } + + public function testSymlinkIsNotOverwrittenIfAlreadyCreated() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; + + touch($file); + symlink($file, $link); + + $this->filesystem->symlink($file, $link); + + $this->assertTrue(is_link($link)); + $this->assertEquals($file, readlink($link)); + } + + public function testSymlinkCreatesTargetDirectoryIfItDoesNotExist() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $link1 = $this->workspace.DIRECTORY_SEPARATOR.'dir'.DIRECTORY_SEPARATOR.'link'; + $link2 = $this->workspace.DIRECTORY_SEPARATOR.'dir'.DIRECTORY_SEPARATOR.'subdir'.DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->symlink($file, $link1); + $this->filesystem->symlink($file, $link2); + + $this->assertTrue(is_link($link1)); + $this->assertEquals($file, readlink($link1)); + $this->assertTrue(is_link($link2)); + $this->assertEquals($file, readlink($link2)); + } + + /** + * @dataProvider providePathsForMakePathRelative + */ + public function testMakePathRelative($endPath, $startPath, $expectedPath) + { + $path = $this->filesystem->makePathRelative($endPath, $startPath); + + $this->assertEquals($expectedPath, $path); + } + + /** + * @return array + */ + public function providePathsForMakePathRelative() + { + $paths = array( + array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/src/Symfony/Component', '../'), + array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/src/Symfony/Component/', '../'), + array('/var/lib/symfony/src/Symfony', '/var/lib/symfony/src/Symfony/Component', '../'), + array('/var/lib/symfony/src/Symfony', '/var/lib/symfony/src/Symfony/Component/', '../'), + array('var/lib/symfony/', 'var/lib/symfony/src/Symfony/Component', '../../../'), + array('/usr/lib/symfony/', '/var/lib/symfony/src/Symfony/Component', '../../../../../../usr/lib/symfony/'), + array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/', 'src/Symfony/'), + array('/aa/bb', '/aa/bb', './'), + array('/aa/bb', '/aa/bb/', './'), + array('/aa/bb/', '/aa/bb', './'), + array('/aa/bb/', '/aa/bb/', './'), + array('/aa/bb/cc', '/aa/bb/cc/dd', '../'), + array('/aa/bb/cc', '/aa/bb/cc/dd/', '../'), + array('/aa/bb/cc/', '/aa/bb/cc/dd', '../'), + array('/aa/bb/cc/', '/aa/bb/cc/dd/', '../'), + array('/aa/bb/cc', '/aa', 'bb/cc/'), + array('/aa/bb/cc', '/aa/', 'bb/cc/'), + array('/aa/bb/cc/', '/aa', 'bb/cc/'), + array('/aa/bb/cc/', '/aa/', 'bb/cc/'), + array('/a/aab/bb', '/a/aa', '../aab/bb/'), + array('/a/aab/bb', '/a/aa/', '../aab/bb/'), + array('/a/aab/bb/', '/a/aa', '../aab/bb/'), + array('/a/aab/bb/', '/a/aa/', '../aab/bb/'), + ); + + if (defined('PHP_WINDOWS_VERSION_MAJOR')) { + $paths[] = array('c:\var\lib/symfony/src/Symfony/', 'c:/var/lib/symfony/', 'src/Symfony/'); + } + + return $paths; + } + + public function testMirrorCopiesFilesAndDirectoriesRecursively() + { + $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR; + $directory = $sourcePath.'directory'.DIRECTORY_SEPARATOR; + $file1 = $directory.'file1'; + $file2 = $sourcePath.'file2'; + + mkdir($sourcePath); + mkdir($directory); + file_put_contents($file1, 'FILE1'); + file_put_contents($file2, 'FILE2'); + + $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR; + + $this->filesystem->mirror($sourcePath, $targetPath); + + $this->assertTrue(is_dir($targetPath)); + $this->assertTrue(is_dir($targetPath.'directory')); + $this->assertFileEquals($file1, $targetPath.'directory'.DIRECTORY_SEPARATOR.'file1'); + $this->assertFileEquals($file2, $targetPath.'file2'); + + $this->filesystem->remove($file1); + + $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => false)); + $this->assertTrue($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1')); + + $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true)); + $this->assertFalse($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1')); + + file_put_contents($file1, 'FILE1'); + + $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true)); + $this->assertTrue($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1')); + + $this->filesystem->remove($directory); + $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true)); + $this->assertFalse($this->filesystem->exists($targetPath.'directory')); + $this->assertFalse($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1')); + } + + public function testMirrorCopiesLinks() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR; + + mkdir($sourcePath); + file_put_contents($sourcePath.'file1', 'FILE1'); + symlink($sourcePath.'file1', $sourcePath.'link1'); + + $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR; + + $this->filesystem->mirror($sourcePath, $targetPath); + + $this->assertTrue(is_dir($targetPath)); + $this->assertFileEquals($sourcePath.'file1', $targetPath.DIRECTORY_SEPARATOR.'link1'); + $this->assertTrue(is_link($targetPath.DIRECTORY_SEPARATOR.'link1')); + } + + public function testMirrorCopiesLinkedDirectoryContents() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR; + + mkdir($sourcePath.'nested/', 0777, true); + file_put_contents($sourcePath.'/nested/file1.txt', 'FILE1'); + // Note: We symlink directory, not file + symlink($sourcePath.'nested', $sourcePath.'link1'); + + $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR; + + $this->filesystem->mirror($sourcePath, $targetPath); + + $this->assertTrue(is_dir($targetPath)); + $this->assertFileEquals($sourcePath.'/nested/file1.txt', $targetPath.DIRECTORY_SEPARATOR.'link1/file1.txt'); + $this->assertTrue(is_link($targetPath.DIRECTORY_SEPARATOR.'link1')); + } + + /** + * @dataProvider providePathsForIsAbsolutePath + */ + public function testIsAbsolutePath($path, $expectedResult) + { + $result = $this->filesystem->isAbsolutePath($path); + + $this->assertEquals($expectedResult, $result); + } + + /** + * @return array + */ + public function providePathsForIsAbsolutePath() + { + return array( + array('/var/lib', true), + array('c:\\\\var\\lib', true), + array('\\var\\lib', true), + array('var/lib', false), + array('../var/lib', false), + array('', false), + array(null, false) + ); + } + + public function testDumpFile() + { + $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt'; + + $this->filesystem->dumpFile($filename, 'bar', 0753); + + $this->assertFileExists($filename); + $this->assertSame('bar', file_get_contents($filename)); + + // skip mode check on windows + if (!defined('PHP_WINDOWS_VERSION_MAJOR')) { + $this->assertEquals(753, $this->getFilePermissions($filename)); + } + } + + public function testDumpFileOverwritesAnExistingFile() + { + $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo.txt'; + file_put_contents($filename, 'FOO BAR'); + + $this->filesystem->dumpFile($filename, 'bar'); + + $this->assertFileExists($filename); + $this->assertSame('bar', file_get_contents($filename)); + } + + /** + * Returns file permissions as three digits (i.e. 755) + * + * @param string $filePath + * + * @return integer + */ + private function getFilePermissions($filePath) + { + return (int) substr(sprintf('%o', fileperms($filePath)), -3); + } + + private function getFileOwner($filepath) + { + $this->markAsSkippedIfPosixIsMissing(); + + $infos = stat($filepath); + if ($datas = posix_getpwuid($infos['uid'])) { + return $datas['name']; + } + } + + private function getFileGroup($filepath) + { + $this->markAsSkippedIfPosixIsMissing(); + + $infos = stat($filepath); + if ($datas = posix_getgrgid($infos['gid'])) { + return $datas['name']; + } + } + + private function markAsSkippedIfSymlinkIsMissing() + { + if (!function_exists('symlink')) { + $this->markTestSkipped('symlink is not supported'); + } + + if (defined('PHP_WINDOWS_VERSION_MAJOR') && false === self::$symlinkOnWindows) { + $this->markTestSkipped('symlink requires "Create symbolic links" privilege on windows'); + } + } + + private function markAsSkippedIfChmodIsMissing() + { + if (defined('PHP_WINDOWS_VERSION_MAJOR')) { + $this->markTestSkipped('chmod is not supported on windows'); + } + } + + private function markAsSkippedIfPosixIsMissing() + { + if (defined('PHP_WINDOWS_VERSION_MAJOR') || !function_exists('posix_isatty')) { + $this->markTestSkipped('Posix is not supported'); + } + } +} diff --git a/vendor/symfony/filesystem/Symfony/Component/Filesystem/composer.json b/vendor/symfony/filesystem/Symfony/Component/Filesystem/composer.json new file mode 100644 index 00000000..167dd506 --- /dev/null +++ b/vendor/symfony/filesystem/Symfony/Component/Filesystem/composer.json @@ -0,0 +1,31 @@ +{ + "name": "symfony/filesystem", + "type": "library", + "description": "Symfony Filesystem Component", + "keywords": [], + "homepage": "http://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\Filesystem\\": "" } + }, + "target-dir": "Symfony/Component/Filesystem", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + } +} diff --git a/vendor/symfony/filesystem/Symfony/Component/Filesystem/phpunit.xml.dist b/vendor/symfony/filesystem/Symfony/Component/Filesystem/phpunit.xml.dist new file mode 100644 index 00000000..ef0bf954 --- /dev/null +++ b/vendor/symfony/filesystem/Symfony/Component/Filesystem/phpunit.xml.dist @@ -0,0 +1,28 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/.gitignore b/vendor/symfony/form/Symfony/Component/Form/.gitignore new file mode 100644 index 00000000..44de97a3 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/.gitignore @@ -0,0 +1,4 @@ +vendor/ +composer.lock +phpunit.xml + diff --git a/vendor/symfony/form/Symfony/Component/Form/AbstractExtension.php b/vendor/symfony/form/Symfony/Component/Form/AbstractExtension.php new file mode 100644 index 00000000..4db77b9b --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/AbstractExtension.php @@ -0,0 +1,195 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\Form\Exception\InvalidArgumentException; +use Symfony\Component\Form\Exception\UnexpectedTypeException; + +/** + * @author Bernhard Schussek + */ +abstract class AbstractExtension implements FormExtensionInterface +{ + /** + * The types provided by this extension + * @var FormTypeInterface[] An array of FormTypeInterface + */ + private $types; + + /** + * The type extensions provided by this extension + * @var FormTypeExtensionInterface[] An array of FormTypeExtensionInterface + */ + private $typeExtensions; + + /** + * The type guesser provided by this extension + * @var FormTypeGuesserInterface + */ + private $typeGuesser; + + /** + * Whether the type guesser has been loaded + * @var Boolean + */ + private $typeGuesserLoaded = false; + + /** + * {@inheritdoc} + */ + public function getType($name) + { + if (null === $this->types) { + $this->initTypes(); + } + + if (!isset($this->types[$name])) { + throw new InvalidArgumentException(sprintf('The type "%s" can not be loaded by this extension', $name)); + } + + return $this->types[$name]; + } + + /** + * {@inheritdoc} + */ + public function hasType($name) + { + if (null === $this->types) { + $this->initTypes(); + } + + return isset($this->types[$name]); + } + + /** + * {@inheritdoc} + */ + public function getTypeExtensions($name) + { + if (null === $this->typeExtensions) { + $this->initTypeExtensions(); + } + + return isset($this->typeExtensions[$name]) + ? $this->typeExtensions[$name] + : array(); + } + + /** + * {@inheritdoc} + */ + public function hasTypeExtensions($name) + { + if (null === $this->typeExtensions) { + $this->initTypeExtensions(); + } + + return isset($this->typeExtensions[$name]) && count($this->typeExtensions[$name]) > 0; + } + + /** + * {@inheritdoc} + */ + public function getTypeGuesser() + { + if (!$this->typeGuesserLoaded) { + $this->initTypeGuesser(); + } + + return $this->typeGuesser; + } + + /** + * Registers the types. + * + * @return FormTypeInterface[] An array of FormTypeInterface instances + */ + protected function loadTypes() + { + return array(); + } + + /** + * Registers the type extensions. + * + * @return FormTypeExtensionInterface[] An array of FormTypeExtensionInterface instances + */ + protected function loadTypeExtensions() + { + return array(); + } + + /** + * Registers the type guesser. + * + * @return FormTypeGuesserInterface|null A type guesser + */ + protected function loadTypeGuesser() + { + return null; + } + + /** + * Initializes the types. + * + * @throws UnexpectedTypeException if any registered type is not an instance of FormTypeInterface + */ + private function initTypes() + { + $this->types = array(); + + foreach ($this->loadTypes() as $type) { + if (!$type instanceof FormTypeInterface) { + throw new UnexpectedTypeException($type, 'Symfony\Component\Form\FormTypeInterface'); + } + + $this->types[$type->getName()] = $type; + } + } + + /** + * Initializes the type extensions. + * + * @throws UnexpectedTypeException if any registered type extension is not + * an instance of FormTypeExtensionInterface + */ + private function initTypeExtensions() + { + $this->typeExtensions = array(); + + foreach ($this->loadTypeExtensions() as $extension) { + if (!$extension instanceof FormTypeExtensionInterface) { + throw new UnexpectedTypeException($extension, 'Symfony\Component\Form\FormTypeExtensionInterface'); + } + + $type = $extension->getExtendedType(); + + $this->typeExtensions[$type][] = $extension; + } + } + + /** + * Initializes the type guesser. + * + * @throws UnexpectedTypeException if the type guesser is not an instance of FormTypeGuesserInterface + */ + private function initTypeGuesser() + { + $this->typeGuesserLoaded = true; + + $this->typeGuesser = $this->loadTypeGuesser(); + if (null !== $this->typeGuesser && !$this->typeGuesser instanceof FormTypeGuesserInterface) { + throw new UnexpectedTypeException($this->typeGuesser, 'Symfony\Component\Form\FormTypeGuesserInterface'); + } + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/AbstractRendererEngine.php b/vendor/symfony/form/Symfony/Component/Form/AbstractRendererEngine.php new file mode 100644 index 00000000..347be10d --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/AbstractRendererEngine.php @@ -0,0 +1,206 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * Default implementation of {@link FormRendererEngineInterface}. + * + * @author Bernhard Schussek + */ +abstract class AbstractRendererEngine implements FormRendererEngineInterface +{ + /** + * The variable in {@link FormView} used as cache key. + */ + const CACHE_KEY_VAR = 'cache_key'; + + /** + * @var array + */ + protected $defaultThemes; + + /** + * @var array + */ + protected $themes = array(); + + /** + * @var array + */ + protected $resources = array(); + + /** + * @var array + */ + private $resourceHierarchyLevels = array(); + + /** + * Creates a new renderer engine. + * + * @param array $defaultThemes The default themes. The type of these + * themes is open to the implementation. + */ + public function __construct(array $defaultThemes = array()) + { + $this->defaultThemes = $defaultThemes; + } + + /** + * {@inheritdoc} + */ + public function setTheme(FormView $view, $themes) + { + $cacheKey = $view->vars[self::CACHE_KEY_VAR]; + + // Do not cast, as casting turns objects into arrays of properties + $this->themes[$cacheKey] = is_array($themes) ? $themes : array($themes); + + // Unset instead of resetting to an empty array, in order to allow + // implementations (like TwigRendererEngine) to check whether $cacheKey + // is set at all. + unset($this->resources[$cacheKey]); + unset($this->resourceHierarchyLevels[$cacheKey]); + } + + /** + * {@inheritdoc} + */ + public function getResourceForBlockName(FormView $view, $blockName) + { + $cacheKey = $view->vars[self::CACHE_KEY_VAR]; + + if (!isset($this->resources[$cacheKey][$blockName])) { + $this->loadResourceForBlockName($cacheKey, $view, $blockName); + } + + return $this->resources[$cacheKey][$blockName]; + } + + /** + * {@inheritdoc} + */ + public function getResourceForBlockNameHierarchy(FormView $view, array $blockNameHierarchy, $hierarchyLevel) + { + $cacheKey = $view->vars[self::CACHE_KEY_VAR]; + $blockName = $blockNameHierarchy[$hierarchyLevel]; + + if (!isset($this->resources[$cacheKey][$blockName])) { + $this->loadResourceForBlockNameHierarchy($cacheKey, $view, $blockNameHierarchy, $hierarchyLevel); + } + + return $this->resources[$cacheKey][$blockName]; + } + + /** + * {@inheritdoc} + */ + public function getResourceHierarchyLevel(FormView $view, array $blockNameHierarchy, $hierarchyLevel) + { + $cacheKey = $view->vars[self::CACHE_KEY_VAR]; + $blockName = $blockNameHierarchy[$hierarchyLevel]; + + if (!isset($this->resources[$cacheKey][$blockName])) { + $this->loadResourceForBlockNameHierarchy($cacheKey, $view, $blockNameHierarchy, $hierarchyLevel); + } + + // If $block was previously rendered loaded with loadTemplateForBlock(), the template + // is cached but the hierarchy level is not. In this case, we know that the block + // exists at this very hierarchy level, so we can just set it. + if (!isset($this->resourceHierarchyLevels[$cacheKey][$blockName])) { + $this->resourceHierarchyLevels[$cacheKey][$blockName] = $hierarchyLevel; + } + + return $this->resourceHierarchyLevels[$cacheKey][$blockName]; + } + + /** + * Loads the cache with the resource for a given block name. + * + * @see getResourceForBlock() + * + * @param string $cacheKey The cache key of the form view. + * @param FormView $view The form view for finding the applying themes. + * @param string $blockName The name of the block to load. + * + * @return Boolean True if the resource could be loaded, false otherwise. + */ + abstract protected function loadResourceForBlockName($cacheKey, FormView $view, $blockName); + + /** + * Loads the cache with the resource for a specific level of a block hierarchy. + * + * @see getResourceForBlockHierarchy() + * + * @param string $cacheKey The cache key used for storing the + * resource. + * @param FormView $view The form view for finding the applying + * themes. + * @param array $blockNameHierarchy The block hierarchy, with the most + * specific block name at the end. + * @param integer $hierarchyLevel The level in the block hierarchy that + * should be loaded. + * + * @return Boolean True if the resource could be loaded, false otherwise. + */ + private function loadResourceForBlockNameHierarchy($cacheKey, FormView $view, array $blockNameHierarchy, $hierarchyLevel) + { + $blockName = $blockNameHierarchy[$hierarchyLevel]; + + // Try to find a template for that block + if ($this->loadResourceForBlockName($cacheKey, $view, $blockName)) { + // If loadTemplateForBlock() returns true, it was able to populate the + // cache. The only missing thing is to set the hierarchy level at which + // the template was found. + $this->resourceHierarchyLevels[$cacheKey][$blockName] = $hierarchyLevel; + + return true; + } + + if ($hierarchyLevel > 0) { + $parentLevel = $hierarchyLevel - 1; + $parentBlockName = $blockNameHierarchy[$parentLevel]; + + // The next two if statements contain slightly duplicated code. This is by intention + // and tries to avoid execution of unnecessary checks in order to increase performance. + + if (isset($this->resources[$cacheKey][$parentBlockName])) { + // It may happen that the parent block is already loaded, but its level is not. + // In this case, the parent block must have been loaded by loadResourceForBlock(), + // which does not check the hierarchy of the block. Subsequently the block must have + // been found directly on the parent level. + if (!isset($this->resourceHierarchyLevels[$cacheKey][$parentBlockName])) { + $this->resourceHierarchyLevels[$cacheKey][$parentBlockName] = $parentLevel; + } + + // Cache the shortcuts for further accesses + $this->resources[$cacheKey][$blockName] = $this->resources[$cacheKey][$parentBlockName]; + $this->resourceHierarchyLevels[$cacheKey][$blockName] = $this->resourceHierarchyLevels[$cacheKey][$parentBlockName]; + + return true; + } + + if ($this->loadResourceForBlockNameHierarchy($cacheKey, $view, $blockNameHierarchy, $parentLevel)) { + // Cache the shortcuts for further accesses + $this->resources[$cacheKey][$blockName] = $this->resources[$cacheKey][$parentBlockName]; + $this->resourceHierarchyLevels[$cacheKey][$blockName] = $this->resourceHierarchyLevels[$cacheKey][$parentBlockName]; + + return true; + } + } + + // Cache the result for further accesses + $this->resources[$cacheKey][$blockName] = false; + $this->resourceHierarchyLevels[$cacheKey][$blockName] = false; + + return false; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/AbstractType.php b/vendor/symfony/form/Symfony/Component/Form/AbstractType.php new file mode 100644 index 00000000..6f7f5da6 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/AbstractType.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +/** + * @author Bernhard Schussek + */ +abstract class AbstractType implements FormTypeInterface +{ + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + } + + /** + * {@inheritdoc} + */ + public function finishView(FormView $view, FormInterface $form, array $options) + { + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'form'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/AbstractTypeExtension.php b/vendor/symfony/form/Symfony/Component/Form/AbstractTypeExtension.php new file mode 100644 index 00000000..351c8009 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/AbstractTypeExtension.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +/** + * @author Bernhard Schussek + */ +abstract class AbstractTypeExtension implements FormTypeExtensionInterface +{ + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + } + + /** + * {@inheritdoc} + */ + public function finishView(FormView $view, FormInterface $form, array $options) + { + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Button.php b/vendor/symfony/form/Symfony/Component/Form/Button.php new file mode 100644 index 00000000..6e12ba16 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Button.php @@ -0,0 +1,436 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\Form\Exception\AlreadySubmittedException; +use Symfony\Component\Form\Exception\BadMethodCallException; + +/** + * A form button. + * + * @author Bernhard Schussek + */ +class Button implements \IteratorAggregate, FormInterface +{ + /** + * @var FormInterface + */ + private $parent; + + /** + * @var FormConfigInterface + */ + private $config; + + /** + * @var Boolean + */ + private $submitted = false; + + /** + * Creates a new button from a form configuration. + * + * @param FormConfigInterface $config The button's configuration. + */ + public function __construct(FormConfigInterface $config) + { + $this->config = $config; + } + + /** + * Unsupported method. + * + * @param mixed $offset + * + * @return Boolean Always returns false. + */ + public function offsetExists($offset) + { + return false; + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param mixed $offset + * + * @throws BadMethodCallException + */ + public function offsetGet($offset) + { + throw new BadMethodCallException('Buttons cannot have children.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param mixed $offset + * @param mixed $value + * + * @throws BadMethodCallException + */ + public function offsetSet($offset, $value) + { + throw new BadMethodCallException('Buttons cannot have children.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param mixed $offset + * + * @throws BadMethodCallException + */ + public function offsetUnset($offset) + { + throw new BadMethodCallException('Buttons cannot have children.'); + } + + /** + * {@inheritdoc} + */ + public function setParent(FormInterface $parent = null) + { + $this->parent = $parent; + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return $this->parent; + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param int|string|FormInterface $child + * @param null $type + * @param array $options + * + * @throws BadMethodCallException + */ + public function add($child, $type = null, array $options = array()) + { + throw new BadMethodCallException('Buttons cannot have children.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param string $name + * + * @throws BadMethodCallException + */ + public function get($name) + { + throw new BadMethodCallException('Buttons cannot have children.'); + } + + /** + * Unsupported method. + * + * @param string $name + * + * @return Boolean Always returns false. + */ + public function has($name) + { + return false; + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param string $name + * + * @throws BadMethodCallException + */ + public function remove($name) + { + throw new BadMethodCallException('Buttons cannot have children.'); + } + + /** + * {@inheritdoc} + */ + public function all() + { + return array(); + } + + /** + * {@inheritdoc} + */ + public function getErrors() + { + return array(); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param string $modelData + * + * @throws BadMethodCallException + */ + public function setData($modelData) + { + throw new BadMethodCallException('Buttons cannot have data.'); + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getData() + { + return null; + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getNormData() + { + return null; + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getViewData() + { + return null; + } + + /** + * Unsupported method. + * + * @return array Always returns an empty array. + */ + public function getExtraData() + { + return array(); + } + + /** + * Returns the button's configuration. + * + * @return FormConfigInterface The configuration. + */ + public function getConfig() + { + return $this->config; + } + + /** + * Returns whether the button is submitted. + * + * @return Boolean true if the button was submitted. + */ + public function isSubmitted() + { + return $this->submitted; + } + + /** + * Returns the name by which the button is identified in forms. + * + * @return string The name of the button. + */ + public function getName() + { + return $this->config->getName(); + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getPropertyPath() + { + return null; + } + + /** + * Unsupported method. + * + * @param FormError $error + * + * @throws BadMethodCallException + */ + public function addError(FormError $error) + { + throw new BadMethodCallException('Buttons cannot have errors.'); + } + + /** + * Unsupported method. + * + * @return Boolean Always returns true. + */ + public function isValid() + { + return true; + } + + /** + * Unsupported method. + * + * @return Boolean Always returns false. + */ + public function isRequired() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function isDisabled() + { + return $this->config->getDisabled(); + } + + /** + * Unsupported method. + * + * @return Boolean Always returns true. + */ + public function isEmpty() + { + return true; + } + + /** + * Unsupported method. + * + * @return Boolean Always returns true. + */ + public function isSynchronized() + { + return true; + } + + /** + * Unsupported method. + * + * @throws BadMethodCallException + */ + public function initialize() + { + throw new BadMethodCallException('Buttons cannot be initialized. Call initialize() on the root form instead.'); + } + + /** + * Unsupported method. + * + * @param mixed $request + * + * @throws BadMethodCallException + */ + public function handleRequest($request = null) + { + throw new BadMethodCallException('Buttons cannot handle requests. Call handleRequest() on the root form instead.'); + } + + /** + * Submits data to the button. + * + * @param null|string $submittedData The data. + * @param Boolean $clearMissing Not used. + * + * @return Button The button instance + * + * @throws Exception\AlreadySubmittedException If the button has already been submitted. + */ + public function submit($submittedData, $clearMissing = true) + { + if ($this->submitted) { + throw new AlreadySubmittedException('A form can only be submitted once'); + } + + $this->submitted = true; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getRoot() + { + return $this->parent ? $this->parent->getRoot() : $this; + } + + /** + * {@inheritdoc} + */ + public function isRoot() + { + return null === $this->parent; + } + + /** + * {@inheritdoc} + */ + public function createView(FormView $parent = null) + { + if (null === $parent && $this->parent) { + $parent = $this->parent->createView(); + } + + return $this->config->getType()->createView($this, $parent); + } + + /** + * Unsupported method. + * + * @return integer Always returns 0. + */ + public function count() + { + return 0; + } + + /** + * Unsupported method. + * + * @return \EmptyIterator Always returns an empty iterator. + */ + public function getIterator() + { + return new \EmptyIterator(); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/ButtonBuilder.php b/vendor/symfony/form/Symfony/Component/Form/ButtonBuilder.php new file mode 100644 index 00000000..3addedbd --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/ButtonBuilder.php @@ -0,0 +1,864 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Form\Exception\InvalidArgumentException; +use Symfony\Component\Form\Exception\BadMethodCallException; + +/** + * A builder for {@link Button} instances. + * + * @author Bernhard Schussek + */ +class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface +{ + /** + * @var Boolean + */ + protected $locked = false; + + /** + * @var Boolean + */ + private $disabled; + + /** + * @var ResolvedFormTypeInterface + */ + private $type; + + /** + * @var string + */ + private $name; + + /** + * @var array + */ + private $attributes = array(); + + /** + * @var array + */ + private $options; + + /** + * Creates a new button builder. + * + * @param string $name The name of the button. + * @param array $options The button's options. + * + * @throws InvalidArgumentException If the name is empty. + */ + public function __construct($name, array $options) + { + if (empty($name) && 0 != $name) { + throw new InvalidArgumentException('Buttons cannot have empty names.'); + } + + $this->name = (string) $name; + $this->options = $options; + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param string|integer|FormBuilderInterface $child + * @param string|FormTypeInterface $type + * @param array $options + * + * @throws BadMethodCallException + */ + public function add($child, $type = null, array $options = array()) + { + throw new BadMethodCallException('Buttons cannot have children.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param string $name + * @param string|FormTypeInterface $type + * @param array $options + * + * @throws BadMethodCallException + */ + public function create($name, $type = null, array $options = array()) + { + throw new BadMethodCallException('Buttons cannot have children.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param string $name + * + * @throws BadMethodCallException + */ + public function get($name) + { + throw new BadMethodCallException('Buttons cannot have children.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param string $name + * + * @throws BadMethodCallException + */ + public function remove($name) + { + throw new BadMethodCallException('Buttons cannot have children.'); + } + + /** + * Unsupported method. + * + * @param string $name + * + * @return Boolean Always returns false. + */ + public function has($name) + { + return false; + } + + /** + * Returns the children. + * + * @return array Always returns an empty array. + */ + public function all() + { + return array(); + } + + /** + * Creates the button. + * + * @return Button The button + */ + public function getForm() + { + return new Button($this->getFormConfig()); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param string $eventName + * @param callable $listener + * @param integer $priority + * + * @throws BadMethodCallException + */ + public function addEventListener($eventName, $listener, $priority = 0) + { + throw new BadMethodCallException('Buttons do not support event listeners.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param EventSubscriberInterface $subscriber + * + * @throws BadMethodCallException + */ + public function addEventSubscriber(EventSubscriberInterface $subscriber) + { + throw new BadMethodCallException('Buttons do not support event subscribers.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param DataTransformerInterface $viewTransformer + * @param Boolean $forcePrepend + * + * @throws BadMethodCallException + */ + public function addViewTransformer(DataTransformerInterface $viewTransformer, $forcePrepend = false) + { + throw new BadMethodCallException('Buttons do not support data transformers.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @throws BadMethodCallException + */ + public function resetViewTransformers() + { + throw new BadMethodCallException('Buttons do not support data transformers.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param DataTransformerInterface $modelTransformer + * @param Boolean $forceAppend + * + * @throws BadMethodCallException + */ + public function addModelTransformer(DataTransformerInterface $modelTransformer, $forceAppend = false) + { + throw new BadMethodCallException('Buttons do not support data transformers.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @throws BadMethodCallException + */ + public function resetModelTransformers() + { + throw new BadMethodCallException('Buttons do not support data transformers.'); + } + + /** + * {@inheritdoc} + */ + public function setAttribute($name, $value) + { + $this->attributes[$name] = $value; + } + + /** + * {@inheritdoc} + */ + public function setAttributes(array $attributes) + { + $this->attributes = $attributes; + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param DataMapperInterface $dataMapper + * + * @throws BadMethodCallException + */ + public function setDataMapper(DataMapperInterface $dataMapper = null) + { + throw new BadMethodCallException('Buttons do not support data mappers.'); + } + + /** + * Set whether the button is disabled. + * + * @param Boolean $disabled Whether the button is disabled + * + * @return ButtonBuilder The button builder. + */ + public function setDisabled($disabled) + { + $this->disabled = $disabled; + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param mixed $emptyData + * + * @throws BadMethodCallException + */ + public function setEmptyData($emptyData) + { + throw new BadMethodCallException('Buttons do not support empty data.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param Boolean $errorBubbling + * + * @throws BadMethodCallException + */ + public function setErrorBubbling($errorBubbling) + { + throw new BadMethodCallException('Buttons do not support error bubbling.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param Boolean $required + * + * @throws BadMethodCallException + */ + public function setRequired($required) + { + throw new BadMethodCallException('Buttons cannot be required.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param null $propertyPath + * + * @throws BadMethodCallException + */ + public function setPropertyPath($propertyPath) + { + throw new BadMethodCallException('Buttons do not support property paths.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param Boolean $mapped + * + * @throws BadMethodCallException + */ + public function setMapped($mapped) + { + throw new BadMethodCallException('Buttons do not support data mapping.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param Boolean $byReference + * + * @throws BadMethodCallException + */ + public function setByReference($byReference) + { + throw new BadMethodCallException('Buttons do not support data mapping.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param Boolean $virtual + * + * @throws BadMethodCallException + */ + public function setVirtual($virtual) + { + throw new BadMethodCallException('Buttons cannot be virtual.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param Boolean $compound + * + * @throws BadMethodCallException + */ + public function setCompound($compound) + { + throw new BadMethodCallException('Buttons cannot be compound.'); + } + + /** + * Sets the type of the button. + * + * @param ResolvedFormTypeInterface $type The type of the button. + * + * @return ButtonBuilder The button builder. + */ + public function setType(ResolvedFormTypeInterface $type) + { + $this->type = $type; + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param array $data + * + * @throws BadMethodCallException + */ + public function setData($data) + { + throw new BadMethodCallException('Buttons do not support data.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param Boolean $locked + * + * @throws BadMethodCallException + */ + public function setDataLocked($locked) + { + throw new BadMethodCallException('Buttons do not support data locking.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param FormFactoryInterface $formFactory + * + * @return void + * + * @throws BadMethodCallException + */ + public function setFormFactory(FormFactoryInterface $formFactory) + { + throw new BadMethodCallException('Buttons do not support form factories.'); + } + + /** + * Unsupported method. + * + * @param string $action + * + * @throws BadMethodCallException + */ + public function setAction($action) + { + throw new BadMethodCallException('Buttons do not support actions.'); + } + + /** + * Unsupported method. + * + * @param string $method + * + * @throws BadMethodCallException + */ + public function setMethod($method) + { + throw new BadMethodCallException('Buttons do not support methods.'); + } + + /** + * Unsupported method. + * + * @param RequestHandlerInterface $requestHandler + * + * @throws BadMethodCallException + */ + public function setRequestHandler(RequestHandlerInterface $requestHandler) + { + throw new BadMethodCallException('Buttons do not support form processors.'); + } + + /** + * Unsupported method. + * + * @param Boolean $initialize + * + * @throws BadMethodCallException + */ + public function setAutoInitialize($initialize) + { + if (true === $initialize) { + throw new BadMethodCallException('Buttons do not support automatic initialization.'); + } + + return $this; + } + + /** + * Unsupported method. + * + * @param Boolean $inheritData + * + * @throws BadMethodCallException + */ + public function setInheritData($inheritData) + { + throw new BadMethodCallException('Buttons do not support data inheritance.'); + } + + /** + * Builds and returns the button configuration. + * + * @return FormConfigInterface + */ + public function getFormConfig() + { + // This method should be idempotent, so clone the builder + $config = clone $this; + $config->locked = true; + + return $config; + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getEventDispatcher() + { + return null; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getPropertyPath() + { + return null; + } + + /** + * Unsupported method. + * + * @return Boolean Always returns false. + */ + public function getMapped() + { + return false; + } + + /** + * Unsupported method. + * + * @return Boolean Always returns false. + */ + public function getByReference() + { + return false; + } + + /** + * Unsupported method. + * + * @return Boolean Always returns false. + */ + public function getVirtual() + { + return false; + } + + /** + * Unsupported method. + * + * @return Boolean Always returns false. + */ + public function getCompound() + { + return false; + } + + /** + * Returns the form type used to construct the button. + * + * @return ResolvedFormTypeInterface The button's type. + */ + public function getType() + { + return $this->type; + } + + /** + * Unsupported method. + * + * @return array Always returns an empty array. + */ + public function getViewTransformers() + { + return array(); + } + + /** + * Unsupported method. + * + * @return array Always returns an empty array. + */ + public function getModelTransformers() + { + return array(); + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getDataMapper() + { + return null; + } + + /** + * Unsupported method. + * + * @return Boolean Always returns false. + */ + public function getRequired() + { + return false; + } + + /** + * Returns whether the button is disabled. + * + * @return Boolean Whether the button is disabled. + */ + public function getDisabled() + { + return $this->disabled; + } + + /** + * Unsupported method. + * + * @return Boolean Always returns false. + */ + public function getErrorBubbling() + { + return false; + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getEmptyData() + { + return null; + } + + /** + * Returns additional attributes of the button. + * + * @return array An array of key-value combinations. + */ + public function getAttributes() + { + return $this->attributes; + } + + /** + * Returns whether the attribute with the given name exists. + * + * @param string $name The attribute name. + * + * @return Boolean Whether the attribute exists. + */ + public function hasAttribute($name) + { + return array_key_exists($name, $this->attributes); + } + + /** + * Returns the value of the given attribute. + * + * @param string $name The attribute name. + * @param mixed $default The value returned if the attribute does not exist. + * + * @return mixed The attribute value. + */ + public function getAttribute($name, $default = null) + { + return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default; + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getData() + { + return null; + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getDataClass() + { + return null; + } + + /** + * Unsupported method. + * + * @return Boolean Always returns false. + */ + public function getDataLocked() + { + return false; + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getFormFactory() + { + return null; + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getAction() + { + return null; + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getMethod() + { + return null; + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getRequestHandler() + { + return null; + } + + /** + * Unsupported method. + * + * @return Boolean Always returns false. + */ + public function getAutoInitialize() + { + return false; + } + + /** + * Unsupported method. + * + * @return Boolean Always returns false. + */ + public function getInheritData() + { + return false; + } + + /** + * Returns all options passed during the construction of the button. + * + * @return array The passed options. + */ + public function getOptions() + { + return $this->options; + } + + /** + * Returns whether a specific option exists. + * + * @param string $name The option name, + * + * @return Boolean Whether the option exists. + */ + public function hasOption($name) + { + return array_key_exists($name, $this->options); + } + + /** + * Returns the value of a specific option. + * + * @param string $name The option name. + * @param mixed $default The value returned if the option does not exist. + * + * @return mixed The option value. + */ + public function getOption($name, $default = null) + { + return array_key_exists($name, $this->options) ? $this->options[$name] : $default; + } + + /** + * Unsupported method. + * + * @return integer Always returns 0. + */ + public function count() + { + return 0; + } + + /** + * Unsupported method. + * + * @return \EmptyIterator Always returns an empty iterator. + */ + public function getIterator() + { + return new \EmptyIterator(); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/ButtonTypeInterface.php b/vendor/symfony/form/Symfony/Component/Form/ButtonTypeInterface.php new file mode 100644 index 00000000..dd5117c4 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/ButtonTypeInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * A type that should be converted into a {@link Button} instance. + * + * @author Bernhard Schussek + */ +interface ButtonTypeInterface extends FormTypeInterface +{ +} diff --git a/vendor/symfony/form/Symfony/Component/Form/CHANGELOG.md b/vendor/symfony/form/Symfony/Component/Form/CHANGELOG.md new file mode 100644 index 00000000..a696c7be --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/CHANGELOG.md @@ -0,0 +1,238 @@ +CHANGELOG +========= + + +2.3.0 +------ + + * deprecated FormPerformanceTestCase and FormIntegrationTestCase in the Symfony\Component\Form\Tests namespace and moved them to the Symfony\Component\Form\Test namespace + * deprecated TypeTestCase in the Symfony\Component\Form\Tests\Extension\Core\Type namespace and moved it to the Symfony\Component\Form\Test namespace + * changed FormRenderer::humanize() to humanize also camel cased field name + * added RequestHandlerInterface and FormInterface::handleRequest() + * deprecated passing a Request instance to FormInterface::bind() + * added options "method" and "action" to FormType + * deprecated option "virtual" in favor "inherit_data" + * deprecated VirtualFormAwareIterator in favor of InheritDataAwareIterator + * [BC BREAK] removed the "array" type hint from DataMapperInterface + * improved forms inheriting their parent data to actually return that data from getData(), getNormData() and getViewData() + * added component-level exceptions for various SPL exceptions + changed all uses of the deprecated Exception class to use more specialized exceptions instead + removed NotInitializedException, NotValidException, TypeDefinitionException, TypeLoaderException, CreationException + * added events PRE_SUBMIT, SUBMIT and POST_SUBMIT + * deprecated events PRE_BIND, BIND and POST_BIND + * [BC BREAK] renamed bind() and isBound() in FormInterface to submit() and isSubmitted() + * added methods submit() and isSubmitted() to Form + * deprecated bind() and isBound() in Form + * deprecated AlreadyBoundException in favor of AlreadySubmittedException + * added support for PATCH requests + * [BC BREAK] added initialize() to FormInterface + * [BC BREAK] added getAutoInitialize() to FormConfigInterface + * [BC BREAK] added setAutoInitialize() to FormConfigBuilderInterface + * [BC BREAK] initialization for Form instances added to a form tree must be manually disabled + * PRE_SET_DATA is now guaranteed to be called after children were added by the form builder, + unless FormInterface::setData() is called manually + * fixed CSRF error message to be translated + * custom CSRF error messages can now be set through the "csrf_message" option + * fixed: expanded single-choice fields now show a radio button for the empty value + +2.2.0 +----- + + * TrimListener now removes unicode whitespaces + * deprecated getParent(), setParent() and hasParent() in FormBuilderInterface + * FormInterface::add() now accepts a FormInterface instance OR a field's name, type and options + * removed special characters between the choice or text fields of DateType unless + the option "format" is set to a custom value + * deprecated FormException and introduced ExceptionInterface instead + * [BC BREAK] FormException is now an interface + * protected FormBuilder methods from being called when it is turned into a FormConfigInterface with getFormConfig() + * [BC BREAK] inserted argument `$message` in the constructor of `FormError` + * the PropertyPath class and related classes were moved to a dedicated + PropertyAccess component. During the move, InvalidPropertyException was + renamed to NoSuchPropertyException. FormUtil was split: FormUtil::singularify() + can now be found in Symfony\Component\PropertyAccess\StringUtil. The methods + getValue() and setValue() from PropertyPath were extracted into a new class + PropertyAccessor. + * added an optional PropertyAccessorInterface parameter to FormType, + ObjectChoiceList and PropertyPathMapper + * [BC BREAK] PropertyPathMapper and FormType now have a constructor + * [BC BREAK] setting the option "validation_groups" to ``false`` now disables validation + instead of assuming group "Default" + +2.1.0 +----- + + * [BC BREAK] ``read_only`` field attribute now renders as ``readonly="readonly"``, use ``disabled`` instead + * [BC BREAK] child forms now aren't validated anymore by default + * made validation of form children configurable (new option: cascade_validation) + * added support for validation groups as callbacks + * made the translation catalogue configurable via the "translation_domain" option + * added Form::getErrorsAsString() to help debugging forms + * allowed setting different options for RepeatedType fields (like the label) + * added support for empty form name at root level, this enables rendering forms + without form name prefix in field names + * [BC BREAK] form and field names must start with a letter, digit or underscore + and only contain letters, digits, underscores, hyphens and colons + * [BC BREAK] changed default name of the prototype in the "collection" type + from "$$name$$" to "\__name\__". No dollars are appended/prepended to custom + names anymore. + * [BC BREAK] improved ChoiceListInterface + * [BC BREAK] added SimpleChoiceList and LazyChoiceList as replacement of + ArrayChoiceList + * added ChoiceList and ObjectChoiceList to use objects as choices + * [BC BREAK] removed EntitiesToArrayTransformer and EntityToIdTransformer. + The former has been replaced by CollectionToArrayTransformer in combination + with EntityChoiceList, the latter is not required in the core anymore. + * [BC BREAK] renamed + * ArrayToBooleanChoicesTransformer to ChoicesToBooleanArrayTransformer + * ScalarToBooleanChoicesTransformer to ChoiceToBooleanArrayTransformer + * ArrayToChoicesTransformer to ChoicesToValuesTransformer + * ScalarToChoiceTransformer to ChoiceToValueTransformer + to be consistent with the naming in ChoiceListInterface. + They were merged into ChoiceList and have no public equivalent anymore. + * choice fields now throw a FormException if neither the "choices" nor the + "choice_list" option is set + * the radio type is now a child of the checkbox type + * the collection, choice (with multiple selection) and entity (with multiple + selection) types now make use of addXxx() and removeXxx() methods in your + model if you set "by_reference" to false. For a custom, non-recognized + singular form, set the "property_path" option like this: "plural|singular" + * forms now don't create an empty object anymore if they are completely + empty and not required. The empty value for such forms is null. + * added constant Guess::VERY_HIGH_CONFIDENCE + * [BC BREAK] The methods `add`, `remove`, `setParent`, `bind` and `setData` + in class Form now throw an exception if the form is already bound + * fields of constrained classes without a NotBlank or NotNull constraint are + set to not required now, as stated in the docs + * fixed TimeType and DateTimeType to not display seconds when "widget" is + "single_text" unless "with_seconds" is set to true + * checkboxes of in an expanded multiple-choice field don't include the choice + in their name anymore. Their names terminate with "[]" now. + * deprecated FormValidatorInterface and substituted its implementations + by event subscribers + * simplified CSRF protection and removed the csrf type + * deprecated FieldType and merged it into FormType + * added new option "compound" that lets you switch between field and form behavior + * [BC BREAK] renamed theme blocks + * "field_*" to "form_*" + * "field_widget" to "form_widget_simple" + * "widget_choice_options" to "choice_widget_options" + * "generic_label" to "form_label" + * added theme blocks "form_widget_compound", "choice_widget_expanded" and + "choice_widget_collapsed" to make theming more modular + * ValidatorTypeGuesser now guesses "collection" for array type constraint + * added method `guessPattern` to FormTypeGuesserInterface to guess which pattern to use in the HTML5 attribute "pattern" + * deprecated method `guessMinLength` in favor of `guessPattern` + * labels don't display field attributes anymore. Label attributes can be + passed in the "label_attr" option/variable + * added option "mapped" which should be used instead of setting "property_path" to false + * [BC BREAK] "data_class" now *must* be set if a form maps to an object and should be left empty otherwise + * improved error mapping on forms + * dot (".") rules are now allowed to map errors assigned to a form to + one of its children + * errors are not mapped to unsynchronized forms anymore + * [BC BREAK] changed Form constructor to accept a single `FormConfigInterface` object + * [BC BREAK] changed argument order in the FormBuilder constructor + * added Form method `getViewData` + * deprecated Form methods + * `getTypes` + * `getErrorBubbling` + * `getNormTransformers` + * `getClientTransformers` + * `getAttribute` + * `hasAttribute` + * `getClientData` + * added FormBuilder methods + * `getTypes` + * `addViewTransformer` + * `getViewTransformers` + * `resetViewTransformers` + * `addModelTransformer` + * `getModelTransformers` + * `resetModelTransformers` + * deprecated FormBuilder methods + * `prependClientTransformer` + * `appendClientTransformer` + * `getClientTransformers` + * `resetClientTransformers` + * `prependNormTransformer` + * `appendNormTransformer` + * `getNormTransformers` + * `resetNormTransformers` + * deprecated the option "validation_constraint" in favor of the new + option "constraints" + * removed superfluous methods from DataMapperInterface + * `mapFormToData` + * `mapDataToForm` + * added `setDefaultOptions` to FormTypeInterface and FormTypeExtensionInterface + which accepts an OptionsResolverInterface instance + * deprecated the methods `getDefaultOptions` and `getAllowedOptionValues` + in FormTypeInterface and FormTypeExtensionInterface + * options passed during construction can now be accessed from FormConfigInterface + * added FormBuilderInterface and FormConfigEditorInterface + * [BC BREAK] the method `buildForm` in FormTypeInterface and FormTypeExtensionInterface + now receives a FormBuilderInterface instead of a FormBuilder instance + * [BC BREAK] the method `buildViewBottomUp` was renamed to `finishView` in + FormTypeInterface and FormTypeExtensionInterface + * [BC BREAK] the options array is now passed as last argument of the + methods + * `buildView` + * `finishView` + in FormTypeInterface and FormTypeExtensionInterface + * [BC BREAK] no options are passed to `getParent` of FormTypeInterface anymore + * deprecated DataEvent and FilterDataEvent in favor of the new FormEvent which is + now passed to all events thrown by the component + * FormEvents::BIND now replaces FormEvents::BIND_NORM_DATA + * FormEvents::PRE_SET_DATA now replaces FormEvents::SET_DATA + * FormEvents::PRE_BIND now replaces FormEvents::BIND_CLIENT_DATA + * deprecated FormEvents::SET_DATA, FormEvents::BIND_CLIENT_DATA and + FormEvents::BIND_NORM_DATA + * [BC BREAK] reversed the order of the first two arguments to `createNamed` + and `createNamedBuilder` in `FormFactoryInterface` + * deprecated `getChildren` in Form and FormBuilder in favor of `all` + * deprecated `hasChildren` in Form and FormBuilder in favor of `count` + * FormBuilder now implements \IteratorAggregate + * [BC BREAK] compound forms now always need a data mapper + * FormBuilder now maintains the order when explicitly adding form builders as children + * ChoiceType now doesn't add the empty value anymore if the choices already contain an empty element + * DateType, TimeType and DateTimeType now show empty values again if not required + * [BC BREAK] fixed rendering of errors for DateType, BirthdayType and similar ones + * [BC BREAK] fixed: form constraints are only validated if they belong to the validated group + * deprecated `bindRequest` in `Form` and replaced it by a listener to FormEvents::PRE_BIND + * fixed: the "data" option supersedes default values from the model + * changed DateType to refer to the "format" option for calculating the year and day choices instead + of padding them automatically + * [BC BREAK] DateType defaults to the format "yyyy-MM-dd" now if the widget is + "single_text", in order to support the HTML 5 date field out of the box + * added the option "format" to DateTimeType + * [BC BREAK] DateTimeType now outputs RFC 3339 dates by default, as generated and + consumed by HTML5 browsers, if the widget is "single_text" + * deprecated the options "data_timezone" and "user_timezone" in DateType, DateTimeType and TimeType + and renamed them to "model_timezone" and "view_timezone" + * fixed: TransformationFailedExceptions thrown in the model transformer are now caught by the form + * added FormRegistryInterface, ResolvedFormTypeInterface and ResolvedFormTypeFactoryInterface + * deprecated FormFactory methods + * `addType` + * `hasType` + * `getType` + * [BC BREAK] FormFactory now expects a FormRegistryInterface and a ResolvedFormTypeFactoryInterface as constructor argument + * [BC BREAK] The method `createBuilder` in FormTypeInterface is not supported anymore for performance reasons + * [BC BREAK] Removed `setTypes` from FormBuilder + * deprecated AbstractType methods + * `getExtensions` + * `setExtensions` + * ChoiceType now caches its created choice lists to improve performance + * [BC BREAK] Rows of a collection field cannot be themed individually anymore. All rows in the collection + field now have the same block names, which contains "entry" where it previously contained the row index. + * [BC BREAK] When registering a type through the DI extension, the tag alias has to match the actual type name. + * added FormRendererInterface, FormRendererEngineInterface and implementations of these interfaces + * [BC BREAK] removed the following methods from FormUtil: + * `toArrayKey` + * `toArrayKeys` + * `isChoiceGroup` + * `isChoiceSelected` + * [BC BREAK] renamed method `renderBlock` in FormHelper to `block` and changed its signature + * made FormView properties public and deprecated their accessor methods + * made the normalized data of a form accessible in the template through the variable "form.vars.data" + * made the original data of a choice accessible in the template through the property "choice.data" + * added convenience class Forms and FormFactoryBuilderInterface diff --git a/vendor/symfony/form/Symfony/Component/Form/CallbackTransformer.php b/vendor/symfony/form/Symfony/Component/Form/CallbackTransformer.php new file mode 100644 index 00000000..1dfe8900 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/CallbackTransformer.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +class CallbackTransformer implements DataTransformerInterface +{ + /** + * The callback used for forward transform + * @var \Closure + */ + private $transform; + + /** + * The callback used for reverse transform + * @var \Closure + */ + private $reverseTransform; + + /** + * Constructor. + * + * @param \Closure $transform The forward transform callback + * @param \Closure $reverseTransform The reverse transform callback + */ + public function __construct(\Closure $transform, \Closure $reverseTransform) + { + $this->transform = $transform; + $this->reverseTransform = $reverseTransform; + } + + /** + * Transforms a value from the original representation to a transformed representation. + * + * @param mixed $data The value in the original representation + * + * @return mixed The value in the transformed representation + * + * @throws UnexpectedTypeException when the argument is not a string + * @throws TransformationFailedException when the transformation fails + */ + public function transform($data) + { + return call_user_func($this->transform, $data); + } + + /** + * Transforms a value from the transformed representation to its original + * representation. + * + * @param mixed $data The value in the transformed representation + * + * @return mixed The value in the original representation + * + * @throws UnexpectedTypeException when the argument is not of the expected type + * @throws TransformationFailedException when the transformation fails + */ + public function reverseTransform($data) + { + return call_user_func($this->reverseTransform, $data); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/ClickableInterface.php b/vendor/symfony/form/Symfony/Component/Form/ClickableInterface.php new file mode 100644 index 00000000..893f02df --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/ClickableInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * A clickable form element. + * + * @author Bernhard Schussek + */ +interface ClickableInterface +{ + /** + * Returns whether this element was clicked. + * + * @return Boolean Whether this element was clicked. + */ + public function isClicked(); +} diff --git a/vendor/symfony/form/Symfony/Component/Form/DataMapperInterface.php b/vendor/symfony/form/Symfony/Component/Form/DataMapperInterface.php new file mode 100644 index 00000000..6e031682 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/DataMapperInterface.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * @author Bernhard Schussek + */ +interface DataMapperInterface +{ + /** + * Maps properties of some data to a list of forms. + * + * @param mixed $data Structured data. + * @param FormInterface[] $forms A list of {@link FormInterface} instances. + * + * @throws Exception\UnexpectedTypeException if the type of the data parameter is not supported. + */ + public function mapDataToForms($data, $forms); + + /** + * Maps the data of a list of forms into the properties of some data. + * + * @param FormInterface[] $forms A list of {@link FormInterface} instances. + * @param mixed $data Structured data. + * + * @throws Exception\UnexpectedTypeException if the type of the data parameter is not supported. + */ + public function mapFormsToData($forms, &$data); +} diff --git a/vendor/symfony/form/Symfony/Component/Form/DataTransformerInterface.php b/vendor/symfony/form/Symfony/Component/Form/DataTransformerInterface.php new file mode 100644 index 00000000..6567da25 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/DataTransformerInterface.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * Transforms a value between different representations. + * + * @author Bernhard Schussek + */ +interface DataTransformerInterface +{ + /** + * Transforms a value from the original representation to a transformed representation. + * + * This method is called on two occasions inside a form field: + * + * 1. When the form field is initialized with the data attached from the datasource (object or array). + * 2. When data from a request is submitted using {@link Form::submit()} to transform the new input data + * back into the renderable format. For example if you have a date field and submit '2009-10-10' + * you might accept this value because its easily parsed, but the transformer still writes back + * "2009/10/10" onto the form field (for further displaying or other purposes). + * + * This method must be able to deal with empty values. Usually this will + * be NULL, but depending on your implementation other empty values are + * possible as well (such as empty strings). The reasoning behind this is + * that value transformers must be chainable. If the transform() method + * of the first value transformer outputs NULL, the second value transformer + * must be able to process that value. + * + * By convention, transform() should return an empty string if NULL is + * passed. + * + * @param mixed $value The value in the original representation + * + * @return mixed The value in the transformed representation + * + * @throws TransformationFailedException When the transformation fails. + */ + public function transform($value); + + /** + * Transforms a value from the transformed representation to its original + * representation. + * + * This method is called when {@link Form::submit()} is called to transform the requests tainted data + * into an acceptable format for your data processing/model layer. + * + * This method must be able to deal with empty values. Usually this will + * be an empty string, but depending on your implementation other empty + * values are possible as well (such as empty strings). The reasoning behind + * this is that value transformers must be chainable. If the + * reverseTransform() method of the first value transformer outputs an + * empty string, the second value transformer must be able to process that + * value. + * + * By convention, reverseTransform() should return NULL if an empty string + * is passed. + * + * @param mixed $value The value in the transformed representation + * + * @return mixed The value in the original representation + * + * @throws TransformationFailedException When the transformation fails. + */ + public function reverseTransform($value); +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Exception/AlreadyBoundException.php b/vendor/symfony/form/Symfony/Component/Form/Exception/AlreadyBoundException.php new file mode 100644 index 00000000..7ef0ca02 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Exception/AlreadyBoundException.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Exception; + +/** + * Alias of {@link AlreadySubmittedException}. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link AlreadySubmittedException} instead. + */ +class AlreadyBoundException extends LogicException +{ +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Exception/AlreadySubmittedException.php b/vendor/symfony/form/Symfony/Component/Form/Exception/AlreadySubmittedException.php new file mode 100644 index 00000000..7be21249 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Exception/AlreadySubmittedException.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Exception; + +/** + * Thrown when an operation is called that is not acceptable after submitting + * a form. + * + * @author Bernhard Schussek + */ +class AlreadySubmittedException extends AlreadyBoundException +{ +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Exception/BadMethodCallException.php b/vendor/symfony/form/Symfony/Component/Form/Exception/BadMethodCallException.php new file mode 100644 index 00000000..27649dd0 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Exception/BadMethodCallException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Exception; + +/** + * Base BadMethodCallException for the Form component. + * + * @author Bernhard Schussek + */ +class BadMethodCallException extends \BadMethodCallException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Exception/ErrorMappingException.php b/vendor/symfony/form/Symfony/Component/Form/Exception/ErrorMappingException.php new file mode 100644 index 00000000..a6968492 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Exception/ErrorMappingException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Exception; + +class ErrorMappingException extends RuntimeException +{ +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Exception/ExceptionInterface.php b/vendor/symfony/form/Symfony/Component/Form/Exception/ExceptionInterface.php new file mode 100644 index 00000000..d455932e --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Exception/ExceptionInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Exception; + +/** + * Base ExceptionInterface for the Form component. + * + * @author Bernhard Schussek + */ +interface ExceptionInterface +{ +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Exception/InvalidArgumentException.php b/vendor/symfony/form/Symfony/Component/Form/Exception/InvalidArgumentException.php new file mode 100644 index 00000000..a270e0ce --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Exception/InvalidArgumentException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Exception; + +/** + * Base InvalidArgumentException for the Form component. + * + * @author Bernhard Schussek + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Exception/InvalidConfigurationException.php b/vendor/symfony/form/Symfony/Component/Form/Exception/InvalidConfigurationException.php new file mode 100644 index 00000000..daa0c42f --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Exception/InvalidConfigurationException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Exception; + +class InvalidConfigurationException extends InvalidArgumentException +{ +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Exception/LogicException.php b/vendor/symfony/form/Symfony/Component/Form/Exception/LogicException.php new file mode 100644 index 00000000..84878021 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Exception/LogicException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Exception; + +/** + * Base LogicException for Form component. + * + * @author Alexander Kotynia + */ +class LogicException extends \LogicException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Exception/OutOfBoundsException.php b/vendor/symfony/form/Symfony/Component/Form/Exception/OutOfBoundsException.php new file mode 100644 index 00000000..44d31166 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Exception/OutOfBoundsException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Exception; + +/** + * Base OutOfBoundsException for Form component. + * + * @author Alexander Kotynia + */ +class OutOfBoundsException extends \OutOfBoundsException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Exception/RuntimeException.php b/vendor/symfony/form/Symfony/Component/Form/Exception/RuntimeException.php new file mode 100644 index 00000000..0af48a4a --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Exception/RuntimeException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Exception; + +/** + * Base RuntimeException for the Form component. + * + * @author Bernhard Schussek + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Exception/StringCastException.php b/vendor/symfony/form/Symfony/Component/Form/Exception/StringCastException.php new file mode 100644 index 00000000..f9b51d60 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Exception/StringCastException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Exception; + +class StringCastException extends RuntimeException +{ +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Exception/TransformationFailedException.php b/vendor/symfony/form/Symfony/Component/Form/Exception/TransformationFailedException.php new file mode 100644 index 00000000..d32896e6 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Exception/TransformationFailedException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Exception; + +/** + * Indicates a value transformation error. + * + * @author Bernhard Schussek + */ +class TransformationFailedException extends RuntimeException +{ +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Exception/UnexpectedTypeException.php b/vendor/symfony/form/Symfony/Component/Form/Exception/UnexpectedTypeException.php new file mode 100644 index 00000000..474e244b --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Exception/UnexpectedTypeException.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Exception; + +class UnexpectedTypeException extends InvalidArgumentException +{ + public function __construct($value, $expectedType) + { + parent::__construct(sprintf('Expected argument of type "%s", "%s" given', $expectedType, is_object($value) ? get_class($value) : gettype($value))); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceList.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceList.php new file mode 100644 index 00000000..f9d381cd --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceList.php @@ -0,0 +1,510 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\ChoiceList; + +use Symfony\Component\Form\FormConfigBuilder; +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\Form\Exception\InvalidConfigurationException; +use Symfony\Component\Form\Exception\InvalidArgumentException; +use Symfony\Component\Form\Extension\Core\View\ChoiceView; + +/** + * A choice list for choices of arbitrary data types. + * + * Choices and labels are passed in two arrays. The indices of the choices + * and the labels should match. Choices may also be given as hierarchy of + * unlimited depth by creating nested arrays. The title of the sub-hierarchy + * can be stored in the array key pointing to the nested array. The topmost + * level of the hierarchy may also be a \Traversable. + * + * + * $choices = array(true, false); + * $labels = array('Agree', 'Disagree'); + * $choiceList = new ChoiceList($choices, $labels); + * + * + * @author Bernhard Schussek + */ +class ChoiceList implements ChoiceListInterface +{ + /** + * The choices with their indices as keys. + * + * @var array + */ + private $choices = array(); + + /** + * The choice values with the indices of the matching choices as keys. + * + * @var array + */ + private $values = array(); + + /** + * The preferred view objects as hierarchy containing also the choice groups + * with the indices of the matching choices as bottom-level keys. + * + * @var array + */ + private $preferredViews = array(); + + /** + * The non-preferred view objects as hierarchy containing also the choice + * groups with the indices of the matching choices as bottom-level keys. + * + * @var array + */ + private $remainingViews = array(); + + /** + * Creates a new choice list. + * + * @param array|\Traversable $choices The array of choices. Choices may also be given + * as hierarchy of unlimited depth. Hierarchies are + * created by creating nested arrays. The title of + * the sub-hierarchy can be stored in the array + * key pointing to the nested array. The topmost + * level of the hierarchy may also be a \Traversable. + * @param array $labels The array of labels. The structure of this array + * should match the structure of $choices. + * @param array $preferredChoices A flat array of choices that should be + * presented to the user with priority. + * + * @throws UnexpectedTypeException If the choices are not an array or \Traversable. + */ + public function __construct($choices, array $labels, array $preferredChoices = array()) + { + if (!is_array($choices) && !$choices instanceof \Traversable) { + throw new UnexpectedTypeException($choices, 'array or \Traversable'); + } + + $this->initialize($choices, $labels, $preferredChoices); + } + + /** + * Initializes the list with choices. + * + * Safe to be called multiple times. The list is cleared on every call. + * + * @param array|\Traversable $choices The choices to write into the list. + * @param array $labels The labels belonging to the choices. + * @param array $preferredChoices The choices to display with priority. + */ + protected function initialize($choices, array $labels, array $preferredChoices) + { + $this->choices = array(); + $this->values = array(); + $this->preferredViews = array(); + $this->remainingViews = array(); + + $this->addChoices( + $this->preferredViews, + $this->remainingViews, + $choices, + $labels, + $preferredChoices + ); + } + + /** + * {@inheritdoc} + */ + public function getChoices() + { + return $this->choices; + } + + /** + * {@inheritdoc} + */ + public function getValues() + { + return $this->values; + } + + /** + * {@inheritdoc} + */ + public function getPreferredViews() + { + return $this->preferredViews; + } + + /** + * {@inheritdoc} + */ + public function getRemainingViews() + { + return $this->remainingViews; + } + + /** + * {@inheritdoc} + */ + public function getChoicesForValues(array $values) + { + $values = $this->fixValues($values); + $choices = array(); + + foreach ($values as $j => $givenValue) { + foreach ($this->values as $i => $value) { + if ($value === $givenValue) { + $choices[] = $this->choices[$i]; + unset($values[$j]); + + if (0 === count($values)) { + break 2; + } + } + } + } + + return $choices; + } + + /** + * {@inheritdoc} + */ + public function getValuesForChoices(array $choices) + { + $choices = $this->fixChoices($choices); + $values = array(); + + foreach ($this->choices as $i => $choice) { + foreach ($choices as $j => $givenChoice) { + if ($choice === $givenChoice) { + $values[] = $this->values[$i]; + unset($choices[$j]); + + if (0 === count($choices)) { + break 2; + } + } + } + } + + return $values; + } + + /** + * {@inheritdoc} + */ + public function getIndicesForChoices(array $choices) + { + $choices = $this->fixChoices($choices); + $indices = array(); + + foreach ($this->choices as $i => $choice) { + foreach ($choices as $j => $givenChoice) { + if ($choice === $givenChoice) { + $indices[] = $i; + unset($choices[$j]); + + if (0 === count($choices)) { + break 2; + } + } + } + } + + return $indices; + } + + /** + * {@inheritdoc} + */ + public function getIndicesForValues(array $values) + { + $values = $this->fixValues($values); + $indices = array(); + + foreach ($this->values as $i => $value) { + foreach ($values as $j => $givenValue) { + if ($value === $givenValue) { + $indices[] = $i; + unset($values[$j]); + + if (0 === count($values)) { + break 2; + } + } + } + } + + return $indices; + } + + /** + * Recursively adds the given choices to the list. + * + * @param array $bucketForPreferred The bucket where to store the preferred + * view objects. + * @param array $bucketForRemaining The bucket where to store the + * non-preferred view objects. + * @param array|\Traversable $choices The list of choices. + * @param array $labels The labels corresponding to the choices. + * @param array $preferredChoices The preferred choices. + * + * @throws InvalidArgumentException If the structures of the choices and labels array do not match. + * @throws InvalidConfigurationException If no valid value or index could be created for a choice. + */ + protected function addChoices(array &$bucketForPreferred, array &$bucketForRemaining, $choices, array $labels, array $preferredChoices) + { + // Add choices to the nested buckets + foreach ($choices as $group => $choice) { + if (!array_key_exists($group, $labels)) { + throw new InvalidArgumentException('The structures of the choices and labels array do not match.'); + } + + if (is_array($choice)) { + // Don't do the work if the array is empty + if (count($choice) > 0) { + $this->addChoiceGroup( + $group, + $bucketForPreferred, + $bucketForRemaining, + $choice, + $labels[$group], + $preferredChoices + ); + } + } else { + $this->addChoice( + $bucketForPreferred, + $bucketForRemaining, + $choice, + $labels[$group], + $preferredChoices + ); + } + } + } + + /** + * Recursively adds a choice group. + * + * @param string $group The name of the group. + * @param array $bucketForPreferred The bucket where to store the preferred + * view objects. + * @param array $bucketForRemaining The bucket where to store the + * non-preferred view objects. + * @param array $choices The list of choices in the group. + * @param array $labels The labels corresponding to the choices in the group. + * @param array $preferredChoices The preferred choices. + * + * @throws InvalidConfigurationException If no valid value or index could be created for a choice. + */ + protected function addChoiceGroup($group, array &$bucketForPreferred, array &$bucketForRemaining, array $choices, array $labels, array $preferredChoices) + { + // If this is a choice group, create a new level in the choice + // key hierarchy + $bucketForPreferred[$group] = array(); + $bucketForRemaining[$group] = array(); + + $this->addChoices( + $bucketForPreferred[$group], + $bucketForRemaining[$group], + $choices, + $labels, + $preferredChoices + ); + + // Remove child levels if empty + if (empty($bucketForPreferred[$group])) { + unset($bucketForPreferred[$group]); + } + if (empty($bucketForRemaining[$group])) { + unset($bucketForRemaining[$group]); + } + } + + /** + * Adds a new choice. + * + * @param array $bucketForPreferred The bucket where to store the preferred + * view objects. + * @param array $bucketForRemaining The bucket where to store the + * non-preferred view objects. + * @param mixed $choice The choice to add. + * @param string $label The label for the choice. + * @param array $preferredChoices The preferred choices. + * + * @throws InvalidConfigurationException If no valid value or index could be created. + */ + protected function addChoice(array &$bucketForPreferred, array &$bucketForRemaining, $choice, $label, array $preferredChoices) + { + $index = $this->createIndex($choice); + + if ('' === $index || null === $index || !FormConfigBuilder::isValidName((string) $index)) { + throw new InvalidConfigurationException(sprintf('The index "%s" created by the choice list is invalid. It should be a valid, non-empty Form name.', $index)); + } + + $value = $this->createValue($choice); + + if (!is_string($value)) { + throw new InvalidConfigurationException(sprintf('The value created by the choice list is of type "%s", but should be a string.', gettype($value))); + } + + $view = new ChoiceView($choice, $value, $label); + + $this->choices[$index] = $this->fixChoice($choice); + $this->values[$index] = $value; + + if ($this->isPreferred($choice, $preferredChoices)) { + $bucketForPreferred[$index] = $view; + } else { + $bucketForRemaining[$index] = $view; + } + } + + /** + * Returns whether the given choice should be preferred judging by the + * given array of preferred choices. + * + * Extension point to optimize performance by changing the structure of the + * $preferredChoices array. + * + * @param mixed $choice The choice to test. + * @param array $preferredChoices An array of preferred choices. + * + * @return Boolean Whether the choice is preferred. + */ + protected function isPreferred($choice, array $preferredChoices) + { + return false !== array_search($choice, $preferredChoices, true); + } + + /** + * Creates a new unique index for this choice. + * + * Extension point to change the indexing strategy. + * + * @param mixed $choice The choice to create an index for + * + * @return integer|string A unique index containing only ASCII letters, + * digits and underscores. + */ + protected function createIndex($choice) + { + return count($this->choices); + } + + /** + * Creates a new unique value for this choice. + * + * By default, an integer is generated since it cannot be guaranteed that + * all values in the list are convertible to (unique) strings. Subclasses + * can override this behaviour if they can guarantee this property. + * + * @param mixed $choice The choice to create a value for + * + * @return string A unique string. + */ + protected function createValue($choice) + { + return (string) count($this->values); + } + + /** + * Fixes the data type of the given choice value to avoid comparison + * problems. + * + * @param mixed $value The choice value. + * + * @return string The value as string. + */ + protected function fixValue($value) + { + return (string) $value; + } + + /** + * Fixes the data types of the given choice values to avoid comparison + * problems. + * + * @param array $values The choice values. + * + * @return array The values as strings. + */ + protected function fixValues(array $values) + { + foreach ($values as $i => $value) { + $values[$i] = $this->fixValue($value); + } + + return $values; + } + + /** + * Fixes the data type of the given choice index to avoid comparison + * problems. + * + * @param mixed $index The choice index. + * + * @return integer|string The index as PHP array key. + */ + protected function fixIndex($index) + { + if (is_bool($index) || (string) (int) $index === (string) $index) { + return (int) $index; + } + + return (string) $index; + } + + /** + * Fixes the data types of the given choice indices to avoid comparison + * problems. + * + * @param array $indices The choice indices. + * + * @return array The indices as strings. + */ + protected function fixIndices(array $indices) + { + foreach ($indices as $i => $index) { + $indices[$i] = $this->fixIndex($index); + } + + return $indices; + } + + /** + * Fixes the data type of the given choice to avoid comparison problems. + * + * Extension point. In this implementation, choices are guaranteed to + * always maintain their type and thus can be typesafely compared. + * + * @param mixed $choice The choice. + * + * @return mixed The fixed choice. + */ + protected function fixChoice($choice) + { + return $choice; + } + + /** + * Fixes the data type of the given choices to avoid comparison problems. + * + * @param array $choices The choices. + * + * @return array The fixed choices. + * + * @see fixChoice + */ + protected function fixChoices(array $choices) + { + return $choices; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceListInterface.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceListInterface.php new file mode 100644 index 00000000..099ace82 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceListInterface.php @@ -0,0 +1,149 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\ChoiceList; + +/** + * Contains choices that can be selected in a form field. + * + * Each choice has three different properties: + * + * - Choice: The choice that should be returned to the application by the + * choice field. Can be any scalar value or an object, but no + * array. + * - Label: A text representing the choice that is displayed to the user. + * - Value: A uniquely identifying value that can contain arbitrary + * characters, but no arrays or objects. This value is displayed + * in the HTML "value" attribute. + * + * @author Bernhard Schussek + */ +interface ChoiceListInterface +{ + /** + * Returns the list of choices + * + * @return array The choices with their indices as keys + */ + public function getChoices(); + + /** + * Returns the values for the choices + * + * @return array The values with the corresponding choice indices as keys + */ + public function getValues(); + + /** + * Returns the choice views of the preferred choices as nested array with + * the choice groups as top-level keys. + * + * Example: + * + * + * array( + * 'Group 1' => array( + * 10 => ChoiceView object, + * 20 => ChoiceView object, + * ), + * 'Group 2' => array( + * 30 => ChoiceView object, + * ), + * ) + * + * + * @return array A nested array containing the views with the corresponding + * choice indices as keys on the lowest levels and the choice + * group names in the keys of the higher levels + */ + public function getPreferredViews(); + + /** + * Returns the choice views of the choices that are not preferred as nested + * array with the choice groups as top-level keys. + * + * Example: + * + * + * array( + * 'Group 1' => array( + * 10 => ChoiceView object, + * 20 => ChoiceView object, + * ), + * 'Group 2' => array( + * 30 => ChoiceView object, + * ), + * ) + * + * + * @return array A nested array containing the views with the corresponding + * choice indices as keys on the lowest levels and the choice + * group names in the keys of the higher levels + * + * @see getPreferredValues + */ + public function getRemainingViews(); + + /** + * Returns the choices corresponding to the given values. + * + * The choices can have any data type. + * + * @param array $values An array of choice values. Not existing values in + * this array are ignored + * + * @return array An array of choices with ascending, 0-based numeric keys + */ + public function getChoicesForValues(array $values); + + /** + * Returns the values corresponding to the given choices. + * + * The values must be strings. + * + * @param array $choices An array of choices. Not existing choices in this + * array are ignored + * + * @return array An array of choice values with ascending, 0-based numeric + * keys + */ + public function getValuesForChoices(array $choices); + + /** + * Returns the indices corresponding to the given choices. + * + * The indices must be positive integers or strings accepted by + * {@link FormConfigBuilder::validateName()}. + * + * The index "placeholder" is internally reserved. + * + * @param array $choices An array of choices. Not existing choices in this + * array are ignored + * + * @return array An array of indices with ascending, 0-based numeric keys + */ + public function getIndicesForChoices(array $choices); + + /** + * Returns the indices corresponding to the given values. + * + * The indices must be positive integers or strings accepted by + * {@link FormConfigBuilder::validateName()}. + * + * The index "placeholder" is internally reserved. + * + * @param array $values An array of choice values. Not existing values in + * this array are ignored + * + * @return array An array of indices with ascending, 0-based numeric keys + */ + public function getIndicesForValues(array $values); +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/LazyChoiceList.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/LazyChoiceList.php new file mode 100644 index 00000000..996f900c --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/LazyChoiceList.php @@ -0,0 +1,149 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\ChoiceList; + +use Symfony\Component\Form\Exception\InvalidArgumentException; + +/** + * A choice list that is loaded lazily + * + * This list loads itself as soon as any of the getters is accessed for the + * first time. You should implement loadChoiceList() in your child classes, + * which should return a ChoiceListInterface instance. + * + * @author Bernhard Schussek + */ +abstract class LazyChoiceList implements ChoiceListInterface +{ + /** + * The loaded choice list + * + * @var ChoiceListInterface + */ + private $choiceList; + + /** + * {@inheritdoc} + */ + public function getChoices() + { + if (!$this->choiceList) { + $this->load(); + } + + return $this->choiceList->getChoices(); + } + + /** + * {@inheritdoc} + */ + public function getValues() + { + if (!$this->choiceList) { + $this->load(); + } + + return $this->choiceList->getValues(); + } + + /** + * {@inheritdoc} + */ + public function getPreferredViews() + { + if (!$this->choiceList) { + $this->load(); + } + + return $this->choiceList->getPreferredViews(); + } + + /** + * {@inheritdoc} + */ + public function getRemainingViews() + { + if (!$this->choiceList) { + $this->load(); + } + + return $this->choiceList->getRemainingViews(); + } + + /** + * {@inheritdoc} + */ + public function getChoicesForValues(array $values) + { + if (!$this->choiceList) { + $this->load(); + } + + return $this->choiceList->getChoicesForValues($values); + } + + /** + * {@inheritdoc} + */ + public function getValuesForChoices(array $choices) + { + if (!$this->choiceList) { + $this->load(); + } + + return $this->choiceList->getValuesForChoices($choices); + } + + /** + * {@inheritdoc} + */ + public function getIndicesForChoices(array $choices) + { + if (!$this->choiceList) { + $this->load(); + } + + return $this->choiceList->getIndicesForChoices($choices); + } + + /** + * {@inheritdoc} + */ + public function getIndicesForValues(array $values) + { + if (!$this->choiceList) { + $this->load(); + } + + return $this->choiceList->getIndicesForValues($values); + } + + /** + * Loads the choice list + * + * Should be implemented by child classes. + * + * @return ChoiceListInterface The loaded choice list + */ + abstract protected function loadChoiceList(); + + private function load() + { + $choiceList = $this->loadChoiceList(); + + if (!$choiceList instanceof ChoiceListInterface) { + throw new InvalidArgumentException(sprintf('loadChoiceList() should return a ChoiceListInterface instance. Got %s', gettype($choiceList))); + } + + $this->choiceList = $choiceList; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ObjectChoiceList.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ObjectChoiceList.php new file mode 100644 index 00000000..0a153883 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ObjectChoiceList.php @@ -0,0 +1,184 @@ + +* +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. +*/ + +namespace Symfony\Component\Form\Extension\Core\ChoiceList; + +use Symfony\Component\Form\Exception\StringCastException; +use Symfony\Component\Form\Exception\InvalidArgumentException; +use Symfony\Component\PropertyAccess\PropertyPath; +use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; +use Symfony\Component\PropertyAccess\PropertyAccess; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; + +/** + * A choice list for object choices. + * + * Supports generation of choice labels, choice groups and choice values + * by calling getters of the object (or associated objects). + * + * + * $choices = array($user1, $user2); + * + * // call getName() to determine the choice labels + * $choiceList = new ObjectChoiceList($choices, 'name'); + * + * + * @author Bernhard Schussek + */ +class ObjectChoiceList extends ChoiceList +{ + /** + * @var PropertyAccessorInterface + */ + private $propertyAccessor; + + /** + * The property path used to obtain the choice label. + * + * @var PropertyPath + */ + private $labelPath; + + /** + * The property path used for object grouping. + * + * @var PropertyPath + */ + private $groupPath; + + /** + * The property path used to obtain the choice value. + * + * @var PropertyPath + */ + private $valuePath; + + /** + * Creates a new object choice list. + * + * @param array|\Traversable $choices The array of choices. Choices may also be given + * as hierarchy of unlimited depth by creating nested + * arrays. The title of the sub-hierarchy can be + * stored in the array key pointing to the nested + * array. The topmost level of the hierarchy may also + * be a \Traversable. + * @param string $labelPath A property path pointing to the property used + * for the choice labels. The value is obtained + * by calling the getter on the object. If the + * path is NULL, the object's __toString() method + * is used instead. + * @param array $preferredChoices A flat array of choices that should be + * presented to the user with priority. + * @param string $groupPath A property path pointing to the property used + * to group the choices. Only allowed if + * the choices are given as flat array. + * @param string $valuePath A property path pointing to the property used + * for the choice values. If not given, integers + * are generated instead. + * @param PropertyAccessorInterface $propertyAccessor The reflection graph for reading property paths. + */ + public function __construct($choices, $labelPath = null, array $preferredChoices = array(), $groupPath = null, $valuePath = null, PropertyAccessorInterface $propertyAccessor = null) + { + $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::getPropertyAccessor(); + $this->labelPath = null !== $labelPath ? new PropertyPath($labelPath) : null; + $this->groupPath = null !== $groupPath ? new PropertyPath($groupPath) : null; + $this->valuePath = null !== $valuePath ? new PropertyPath($valuePath) : null; + + parent::__construct($choices, array(), $preferredChoices); + } + + /** + * Initializes the list with choices. + * + * Safe to be called multiple times. The list is cleared on every call. + * + * @param array|\Traversable $choices The choices to write into the list. + * @param array $labels Ignored. + * @param array $preferredChoices The choices to display with priority. + * + * @throws InvalidArgumentException When passing a hierarchy of choices and using + * the "groupPath" option at the same time. + */ + protected function initialize($choices, array $labels, array $preferredChoices) + { + if (null !== $this->groupPath) { + $groupedChoices = array(); + + foreach ($choices as $i => $choice) { + if (is_array($choice)) { + throw new InvalidArgumentException('You should pass a plain object array (without groups) when using the "groupPath" option.'); + } + + try { + $group = $this->propertyAccessor->getValue($choice, $this->groupPath); + } catch (NoSuchPropertyException $e) { + // Don't group items whose group property does not exist + // see https://github.com/symfony/symfony/commit/d9b7abb7c7a0f28e0ce970afc5e305dce5dccddf + $group = null; + } + + if (null === $group) { + $groupedChoices[$i] = $choice; + } else { + if (!isset($groupedChoices[$group])) { + $groupedChoices[$group] = array(); + } + + $groupedChoices[$group][$i] = $choice; + } + } + + $choices = $groupedChoices; + } + + $labels = array(); + + $this->extractLabels($choices, $labels); + + parent::initialize($choices, $labels, $preferredChoices); + } + + /** + * Creates a new unique value for this choice. + * + * If a property path for the value was given at object creation, + * the getter behind that path is now called to obtain a new value. + * Otherwise a new integer is generated. + * + * @param mixed $choice The choice to create a value for + * + * @return integer|string A unique value without character limitations. + */ + protected function createValue($choice) + { + if ($this->valuePath) { + return (string) $this->propertyAccessor->getValue($choice, $this->valuePath); + } + + return parent::createValue($choice); + } + + private function extractLabels($choices, array &$labels) + { + foreach ($choices as $i => $choice) { + if (is_array($choice)) { + $labels[$i] = array(); + $this->extractLabels($choice, $labels[$i]); + } elseif ($this->labelPath) { + $labels[$i] = $this->propertyAccessor->getValue($choice, $this->labelPath); + } elseif (method_exists($choice, '__toString')) { + $labels[$i] = (string) $choice; + } else { + throw new StringCastException(sprintf('A "__toString()" method was not found on the objects of type "%s" passed to the choice field. To read a custom getter instead, set the argument $labelPath to the desired property path.', get_class($choice))); + } + } + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/SimpleChoiceList.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/SimpleChoiceList.php new file mode 100644 index 00000000..914dbe5f --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/SimpleChoiceList.php @@ -0,0 +1,164 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\ChoiceList; + +/** + * A choice list for choices of type string or integer. + * + * Choices and their associated labels can be passed in a single array. Since + * choices are passed as array keys, only strings or integer choices are + * allowed. Choices may also be given as hierarchy of unlimited depth by + * creating nested arrays. The title of the sub-hierarchy can be stored in the + * array key pointing to the nested array. + * + * + * $choiceList = new SimpleChoiceList(array( + * 'creditcard' => 'Credit card payment', + * 'cash' => 'Cash payment', + * )); + * + * + * @author Bernhard Schussek + */ +class SimpleChoiceList extends ChoiceList +{ + /** + * Creates a new simple choice list. + * + * @param array $choices The array of choices with the choices as keys and + * the labels as values. Choices may also be given + * as hierarchy of unlimited depth by creating nested + * arrays. The title of the sub-hierarchy is stored + * in the array key pointing to the nested array. + * @param array $preferredChoices A flat array of choices that should be + * presented to the user with priority. + */ + public function __construct(array $choices, array $preferredChoices = array()) + { + // Flip preferred choices to speed up lookup + parent::__construct($choices, $choices, array_flip($preferredChoices)); + } + + /** + * {@inheritdoc} + */ + public function getChoicesForValues(array $values) + { + $values = $this->fixValues($values); + + // The values are identical to the choices, so we can just return them + // to improve performance a little bit + return $this->fixChoices(array_intersect($values, $this->getValues())); + } + + /** + * {@inheritdoc} + */ + public function getValuesForChoices(array $choices) + { + $choices = $this->fixChoices($choices); + + // The choices are identical to the values, so we can just return them + // to improve performance a little bit + return $this->fixValues(array_intersect($choices, $this->getValues())); + } + + /** + * Recursively adds the given choices to the list. + * + * Takes care of splitting the single $choices array passed in the + * constructor into choices and labels. + * + * @param array $bucketForPreferred The bucket where to store the preferred + * view objects. + * @param array $bucketForRemaining The bucket where to store the + * non-preferred view objects. + * @param array|\Traversable $choices The list of choices. + * @param array $labels Ignored. + * @param array $preferredChoices The preferred choices. + */ + protected function addChoices(array &$bucketForPreferred, array &$bucketForRemaining, $choices, array $labels, array $preferredChoices) + { + // Add choices to the nested buckets + foreach ($choices as $choice => $label) { + if (is_array($label)) { + // Don't do the work if the array is empty + if (count($label) > 0) { + $this->addChoiceGroup( + $choice, + $bucketForPreferred, + $bucketForRemaining, + $label, + $label, + $preferredChoices + ); + } + } else { + $this->addChoice( + $bucketForPreferred, + $bucketForRemaining, + $choice, + $label, + $preferredChoices + ); + } + } + } + + /** + * Returns whether the given choice should be preferred judging by the + * given array of preferred choices. + * + * Optimized for performance by treating the preferred choices as array + * where choices are stored in the keys. + * + * @param mixed $choice The choice to test. + * @param array $preferredChoices An array of preferred choices. + * + * @return Boolean Whether the choice is preferred. + */ + protected function isPreferred($choice, array $preferredChoices) + { + // Optimize performance over the default implementation + return isset($preferredChoices[$choice]); + } + + /** + * Converts the choice to a valid PHP array key. + * + * @param mixed $choice The choice. + * + * @return string|integer A valid PHP array key. + */ + protected function fixChoice($choice) + { + return $this->fixIndex($choice); + } + + /** + * {@inheritdoc} + */ + protected function fixChoices(array $choices) + { + return $this->fixIndices($choices); + } + + /** + * {@inheritdoc} + */ + protected function createValue($choice) + { + // Choices are guaranteed to be unique and scalar, so we can simply + // convert them to strings + return (string) $choice; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/CoreExtension.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/CoreExtension.php new file mode 100644 index 00000000..bbcac4ba --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/CoreExtension.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core; + +use Symfony\Component\Form\AbstractExtension; +use Symfony\Component\PropertyAccess\PropertyAccess; + +/** + * Represents the main form extension, which loads the core functionality. + * + * @author Bernhard Schussek + */ +class CoreExtension extends AbstractExtension +{ + protected function loadTypes() + { + return array( + new Type\FormType(PropertyAccess::getPropertyAccessor()), + new Type\BirthdayType(), + new Type\CheckboxType(), + new Type\ChoiceType(), + new Type\CollectionType(), + new Type\CountryType(), + new Type\DateType(), + new Type\DateTimeType(), + new Type\EmailType(), + new Type\HiddenType(), + new Type\IntegerType(), + new Type\LanguageType(), + new Type\LocaleType(), + new Type\MoneyType(), + new Type\NumberType(), + new Type\PasswordType(), + new Type\PercentType(), + new Type\RadioType(), + new Type\RepeatedType(), + new Type\SearchType(), + new Type\TextareaType(), + new Type\TextType(), + new Type\TimeType(), + new Type\TimezoneType(), + new Type\UrlType(), + new Type\FileType(), + new Type\ButtonType(), + new Type\SubmitType(), + new Type\ResetType(), + new Type\CurrencyType(), + ); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php new file mode 100644 index 00000000..d8bd9c71 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataMapper; + +use Symfony\Component\Form\DataMapperInterface; +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\PropertyAccess\PropertyAccess; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; + +/** + * A data mapper using property paths to read/write data. + * + * @author Bernhard Schussek + */ +class PropertyPathMapper implements DataMapperInterface +{ + /** + * @var PropertyAccessorInterface + */ + private $propertyAccessor; + + /** + * Creates a new property path mapper. + * + * @param PropertyAccessorInterface $propertyAccessor + */ + public function __construct(PropertyAccessorInterface $propertyAccessor = null) + { + $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::getPropertyAccessor(); + } + + /** + * {@inheritdoc} + */ + public function mapDataToForms($data, $forms) + { + if (null === $data || array() === $data) { + return; + } + + if (!is_array($data) && !is_object($data)) { + throw new UnexpectedTypeException($data, 'object, array or empty'); + } + + foreach ($forms as $form) { + $propertyPath = $form->getPropertyPath(); + $config = $form->getConfig(); + + if (null !== $propertyPath && $config->getMapped()) { + $form->setData($this->propertyAccessor->getValue($data, $propertyPath)); + } + } + } + + /** + * {@inheritdoc} + */ + public function mapFormsToData($forms, &$data) + { + if (null === $data) { + return; + } + + if (!is_array($data) && !is_object($data)) { + throw new UnexpectedTypeException($data, 'object, array or empty'); + } + + foreach ($forms as $form) { + $propertyPath = $form->getPropertyPath(); + $config = $form->getConfig(); + + // Write-back is disabled if the form is not synchronized (transformation failed) + // and if the form is disabled (modification not allowed) + if (null !== $propertyPath && $config->getMapped() && $form->isSynchronized() && !$form->isDisabled()) { + // If the data is identical to the value in $data, we are + // dealing with a reference + if (!is_object($data) || !$config->getByReference() || $form->getData() !== $this->propertyAccessor->getValue($data, $propertyPath)) { + $this->propertyAccessor->setValue($data, $propertyPath, $form->getData()); + } + } + } + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ArrayToPartsTransformer.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ArrayToPartsTransformer.php new file mode 100644 index 00000000..fc080f25 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ArrayToPartsTransformer.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * @author Bernhard Schussek + */ +class ArrayToPartsTransformer implements DataTransformerInterface +{ + private $partMapping; + + public function __construct(array $partMapping) + { + $this->partMapping = $partMapping; + } + + public function transform($array) + { + if (null === $array) { + $array = array(); + } + + if (!is_array($array) ) { + throw new TransformationFailedException('Expected an array.'); + } + + $result = array(); + + foreach ($this->partMapping as $partKey => $originalKeys) { + if (empty($array)) { + $result[$partKey] = null; + } else { + $result[$partKey] = array_intersect_key($array, array_flip($originalKeys)); + } + } + + return $result; + } + + public function reverseTransform($array) + { + if (!is_array($array) ) { + throw new TransformationFailedException('Expected an array.'); + } + + $result = array(); + $emptyKeys = array(); + + foreach ($this->partMapping as $partKey => $originalKeys) { + if (!empty($array[$partKey])) { + foreach ($originalKeys as $originalKey) { + if (isset($array[$partKey][$originalKey])) { + $result[$originalKey] = $array[$partKey][$originalKey]; + } + } + } else { + $emptyKeys[] = $partKey; + } + } + + if (count($emptyKeys) > 0) { + if (count($emptyKeys) === count($this->partMapping)) { + // All parts empty + return null; + } + + throw new TransformationFailedException( + sprintf('The keys "%s" should not be empty', implode('", "', $emptyKeys) + )); + } + + return $result; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/BaseDateTimeTransformer.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/BaseDateTimeTransformer.php new file mode 100644 index 00000000..e4e8932e --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/BaseDateTimeTransformer.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\UnexpectedTypeException; + +abstract class BaseDateTimeTransformer implements DataTransformerInterface +{ + protected static $formats = array( + \IntlDateFormatter::NONE, + \IntlDateFormatter::FULL, + \IntlDateFormatter::LONG, + \IntlDateFormatter::MEDIUM, + \IntlDateFormatter::SHORT, + ); + + protected $inputTimezone; + + protected $outputTimezone; + + /** + * Constructor. + * + * @param string $inputTimezone The name of the input timezone + * @param string $outputTimezone The name of the output timezone + * + * @throws UnexpectedTypeException if a timezone is not a string + */ + public function __construct($inputTimezone = null, $outputTimezone = null) + { + if (!is_string($inputTimezone) && null !== $inputTimezone) { + throw new UnexpectedTypeException($inputTimezone, 'string'); + } + + if (!is_string($outputTimezone) && null !== $outputTimezone) { + throw new UnexpectedTypeException($outputTimezone, 'string'); + } + + $this->inputTimezone = $inputTimezone ?: date_default_timezone_get(); + $this->outputTimezone = $outputTimezone ?: date_default_timezone_get(); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/BooleanToStringTransformer.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/BooleanToStringTransformer.php new file mode 100644 index 00000000..95e7332d --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/BooleanToStringTransformer.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * Transforms between a Boolean and a string. + * + * @author Bernhard Schussek + * @author Florian Eckerstorfer + */ +class BooleanToStringTransformer implements DataTransformerInterface +{ + /** + * The value emitted upon transform if the input is true + * @var string + */ + private $trueValue; + + /** + * Sets the value emitted upon transform if the input is true. + * + * @param string $trueValue + */ + public function __construct($trueValue) + { + $this->trueValue = $trueValue; + } + + /** + * Transforms a Boolean into a string. + * + * @param Boolean $value Boolean value. + * + * @return string String value. + * + * @throws TransformationFailedException If the given value is not a Boolean. + */ + public function transform($value) + { + if (null === $value) { + return null; + } + + if (!is_bool($value)) { + throw new TransformationFailedException('Expected a Boolean.'); + } + + return true === $value ? $this->trueValue : null; + } + + /** + * Transforms a string into a Boolean. + * + * @param string $value String value. + * + * @return Boolean Boolean value. + * + * @throws TransformationFailedException If the given value is not a string. + */ + public function reverseTransform($value) + { + if (null === $value) { + return false; + } + + if (!is_string($value)) { + throw new TransformationFailedException('Expected a string.'); + } + + return true; + } + +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ChoiceToBooleanArrayTransformer.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ChoiceToBooleanArrayTransformer.php new file mode 100644 index 00000000..79b3f7ac --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ChoiceToBooleanArrayTransformer.php @@ -0,0 +1,118 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface; +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * @author Bernhard Schussek + */ +class ChoiceToBooleanArrayTransformer implements DataTransformerInterface +{ + private $choiceList; + + private $placeholderPresent; + + /** + * Constructor. + * + * @param ChoiceListInterface $choiceList + * @param Boolean $placeholderPresent + */ + public function __construct(ChoiceListInterface $choiceList, $placeholderPresent) + { + $this->choiceList = $choiceList; + $this->placeholderPresent = $placeholderPresent; + } + + /** + * Transforms a single choice to a format appropriate for the nested + * checkboxes/radio buttons. + * + * The result is an array with the options as keys and true/false as values, + * depending on whether a given option is selected. If this field is rendered + * as select tag, the value is not modified. + * + * @param mixed $choice An array if "multiple" is set to true, a scalar + * value otherwise. + * + * @return mixed An array + * + * @throws TransformationFailedException If the given value is not scalar or + * if the choices can not be retrieved. + */ + public function transform($choice) + { + try { + $values = $this->choiceList->getValues(); + } catch (\Exception $e) { + throw new TransformationFailedException('Can not get the choice list', $e->getCode(), $e); + } + + $index = current($this->choiceList->getIndicesForChoices(array($choice))); + + foreach ($values as $i => $value) { + $values[$i] = $i === $index; + } + + if ($this->placeholderPresent) { + $values['placeholder'] = false === $index; + } + + return $values; + } + + /** + * Transforms a checkbox/radio button array to a single choice. + * + * The input value is an array with the choices as keys and true/false as + * values, depending on whether a given choice is selected. The output + * is the selected choice. + * + * @param array $values An array of values + * + * @return mixed A scalar value + * + * @throws TransformationFailedException If the given value is not an array, + * if the recuperation of the choices + * fails or if some choice can't be + * found. + */ + public function reverseTransform($values) + { + if (!is_array($values)) { + throw new TransformationFailedException('Expected an array.'); + } + + try { + $choices = $this->choiceList->getChoices(); + } catch (\Exception $e) { + throw new TransformationFailedException('Can not get the choice list', $e->getCode(), $e); + } + + foreach ($values as $i => $selected) { + if ($selected) { + if (isset($choices[$i])) { + return $choices[$i] === '' ? null : $choices[$i]; + } elseif ($this->placeholderPresent && 'placeholder' === $i) { + return null; + } else { + throw new TransformationFailedException(sprintf('The choice "%s" does not exist', $i)); + } + } + } + + return null; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ChoiceToValueTransformer.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ChoiceToValueTransformer.php new file mode 100644 index 00000000..5a818558 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ChoiceToValueTransformer.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface; + +/** + * @author Bernhard Schussek + */ +class ChoiceToValueTransformer implements DataTransformerInterface +{ + private $choiceList; + + /** + * Constructor. + * + * @param ChoiceListInterface $choiceList + */ + public function __construct(ChoiceListInterface $choiceList) + { + $this->choiceList = $choiceList; + } + + public function transform($choice) + { + return (string) current($this->choiceList->getValuesForChoices(array($choice))); + } + + public function reverseTransform($value) + { + if (null !== $value && !is_scalar($value)) { + throw new TransformationFailedException('Expected a scalar.'); + } + + // These are now valid ChoiceList values, so we can return null + // right away + if ('' === $value || null === $value) { + return null; + } + + $choices = $this->choiceList->getChoicesForValues(array($value)); + + if (1 !== count($choices)) { + throw new TransformationFailedException(sprintf('The choice "%s" does not exist or is not unique', $value)); + } + + $choice = current($choices); + + return '' === $choice ? null : $choice; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ChoicesToBooleanArrayTransformer.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ChoicesToBooleanArrayTransformer.php new file mode 100644 index 00000000..a13c0d4d --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ChoicesToBooleanArrayTransformer.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface; +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * @author Bernhard Schussek + */ +class ChoicesToBooleanArrayTransformer implements DataTransformerInterface +{ + private $choiceList; + + public function __construct(ChoiceListInterface $choiceList) + { + $this->choiceList = $choiceList; + } + + /** + * Transforms an array of choices to a format appropriate for the nested + * checkboxes/radio buttons. + * + * The result is an array with the options as keys and true/false as values, + * depending on whether a given option is selected. If this field is rendered + * as select tag, the value is not modified. + * + * @param mixed $array An array + * + * @return mixed An array + * + * @throws TransformationFailedException If the given value is not an array + * or if the choices can not be retrieved. + */ + public function transform($array) + { + if (null === $array) { + return array(); + } + + if (!is_array($array)) { + throw new TransformationFailedException('Expected an array.'); + } + + try { + $values = $this->choiceList->getValues(); + } catch (\Exception $e) { + throw new TransformationFailedException('Can not get the choice list', $e->getCode(), $e); + } + + $indexMap = array_flip($this->choiceList->getIndicesForChoices($array)); + + foreach ($values as $i => $value) { + $values[$i] = isset($indexMap[$i]); + } + + return $values; + } + + /** + * Transforms a checkbox/radio button array to an array of choices. + * + * The input value is an array with the choices as keys and true/false as + * values, depending on whether a given choice is selected. The output + * is an array with the selected choices. + * + * @param mixed $values An array + * + * @return mixed An array + * + * @throws TransformationFailedException If the given value is not an array, + * if the recuperation of the choices + * fails or if some choice can't be + * found. + */ + public function reverseTransform($values) + { + if (!is_array($values)) { + throw new TransformationFailedException('Expected an array.'); + } + + try { + $choices = $this->choiceList->getChoices(); + } catch (\Exception $e) { + throw new TransformationFailedException('Can not get the choice list', $e->getCode(), $e); + } + + $result = array(); + $unknown = array(); + + foreach ($values as $i => $selected) { + if ($selected) { + if (isset($choices[$i])) { + $result[] = $choices[$i]; + } else { + $unknown[] = $i; + } + } + } + + if (count($unknown) > 0) { + throw new TransformationFailedException(sprintf('The choices "%s" were not found', implode('", "', $unknown))); + } + + return $result; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ChoicesToValuesTransformer.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ChoicesToValuesTransformer.php new file mode 100644 index 00000000..4492865e --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ChoicesToValuesTransformer.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Exception\TransformationFailedException; + +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface; + +/** + * @author Bernhard Schussek + */ +class ChoicesToValuesTransformer implements DataTransformerInterface +{ + private $choiceList; + + /** + * Constructor. + * + * @param ChoiceListInterface $choiceList + */ + public function __construct(ChoiceListInterface $choiceList) + { + $this->choiceList = $choiceList; + } + + /** + * @param array $array + * + * @return array + * + * @throws TransformationFailedException If the given value is not an array. + */ + public function transform($array) + { + if (null === $array) { + return array(); + } + + if (!is_array($array)) { + throw new TransformationFailedException('Expected an array.'); + } + + return $this->choiceList->getValuesForChoices($array); + } + + /** + * @param array $array + * + * @return array + * + * @throws TransformationFailedException If the given value is not an array + * or if no matching choice could be + * found for some given value. + */ + public function reverseTransform($array) + { + if (null === $array) { + return array(); + } + + if (!is_array($array)) { + throw new TransformationFailedException('Expected an array.'); + } + + $choices = $this->choiceList->getChoicesForValues($array); + + if (count($choices) !== count($array)) { + throw new TransformationFailedException('Could not find all matching choices for the given values'); + } + + return $choices; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DataTransformerChain.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DataTransformerChain.php new file mode 100644 index 00000000..9cc185e1 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DataTransformerChain.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * Passes a value through multiple value transformers + * + * @author Bernhard Schussek + */ +class DataTransformerChain implements DataTransformerInterface +{ + /** + * The value transformers + * @var DataTransformerInterface[] + */ + protected $transformers; + + /** + * Uses the given value transformers to transform values + * + * @param array $transformers + */ + public function __construct(array $transformers) + { + $this->transformers = $transformers; + } + + /** + * Passes the value through the transform() method of all nested transformers + * + * The transformers receive the value in the same order as they were passed + * to the constructor. Each transformer receives the result of the previous + * transformer as input. The output of the last transformer is returned + * by this method. + * + * @param mixed $value The original value + * + * @return mixed The transformed value + * + * @throws TransformationFailedException + */ + public function transform($value) + { + foreach ($this->transformers as $transformer) { + $value = $transformer->transform($value); + } + + return $value; + } + + /** + * Passes the value through the reverseTransform() method of all nested + * transformers + * + * The transformers receive the value in the reverse order as they were passed + * to the constructor. Each transformer receives the result of the previous + * transformer as input. The output of the last transformer is returned + * by this method. + * + * @param mixed $value The transformed value + * + * @return mixed The reverse-transformed value + * + * @throws TransformationFailedException + */ + public function reverseTransform($value) + { + for ($i = count($this->transformers) - 1; $i >= 0; --$i) { + $value = $this->transformers[$i]->reverseTransform($value); + } + + return $value; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php new file mode 100644 index 00000000..34af2820 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php @@ -0,0 +1,184 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\Exception\UnexpectedTypeException; + +/** + * Transforms between a normalized time and a localized time string/array. + * + * @author Bernhard Schussek + * @author Florian Eckerstorfer + */ +class DateTimeToArrayTransformer extends BaseDateTimeTransformer +{ + private $pad; + + private $fields; + + /** + * Constructor. + * + * @param string $inputTimezone The input timezone + * @param string $outputTimezone The output timezone + * @param array $fields The date fields + * @param Boolean $pad Whether to use padding + * + * @throws UnexpectedTypeException if a timezone is not a string + */ + public function __construct($inputTimezone = null, $outputTimezone = null, array $fields = null, $pad = false) + { + parent::__construct($inputTimezone, $outputTimezone); + + if (null === $fields) { + $fields = array('year', 'month', 'day', 'hour', 'minute', 'second'); + } + + $this->fields = $fields; + $this->pad = (Boolean) $pad; + } + + /** + * Transforms a normalized date into a localized date. + * + * @param \DateTime $dateTime Normalized date. + * + * @return array Localized date. + * + * @throws TransformationFailedException If the given value is not an + * instance of \DateTime or if the + * output timezone is not supported. + */ + public function transform($dateTime) + { + if (null === $dateTime) { + return array_intersect_key(array( + 'year' => '', + 'month' => '', + 'day' => '', + 'hour' => '', + 'minute' => '', + 'second' => '', + ), array_flip($this->fields)); + } + + if (!$dateTime instanceof \DateTime) { + throw new TransformationFailedException('Expected a \DateTime.'); + } + + $dateTime = clone $dateTime; + if ($this->inputTimezone !== $this->outputTimezone) { + try { + $dateTime->setTimezone(new \DateTimeZone($this->outputTimezone)); + } catch (\Exception $e) { + throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); + } + } + + $result = array_intersect_key(array( + 'year' => $dateTime->format('Y'), + 'month' => $dateTime->format('m'), + 'day' => $dateTime->format('d'), + 'hour' => $dateTime->format('H'), + 'minute' => $dateTime->format('i'), + 'second' => $dateTime->format('s'), + ), array_flip($this->fields)); + + if (!$this->pad) { + foreach ($result as &$entry) { + // remove leading zeros + $entry = (string) (int) $entry; + } + } + + return $result; + } + + /** + * Transforms a localized date into a normalized date. + * + * @param array $value Localized date + * + * @return \DateTime Normalized date + * + * @throws TransformationFailedException If the given value is not an array, + * if the value could not be transformed + * or if the input timezone is not + * supported. + */ + public function reverseTransform($value) + { + if (null === $value) { + return null; + } + + if (!is_array($value)) { + throw new TransformationFailedException('Expected an array.'); + } + + if ('' === implode('', $value)) { + return null; + } + + $emptyFields = array(); + + foreach ($this->fields as $field) { + if (!isset($value[$field])) { + $emptyFields[] = $field; + } + } + + if (count($emptyFields) > 0) { + throw new TransformationFailedException( + sprintf('The fields "%s" should not be empty', implode('", "', $emptyFields) + )); + } + + if (isset($value['month']) && !ctype_digit($value['month']) && !is_int($value['month'])) { + throw new TransformationFailedException('This month is invalid'); + } + + if (isset($value['day']) && !ctype_digit($value['day']) && !is_int($value['day'])) { + throw new TransformationFailedException('This day is invalid'); + } + + if (isset($value['year']) && !ctype_digit($value['year']) && !is_int($value['year'])) { + throw new TransformationFailedException('This year is invalid'); + } + + if (!empty($value['month']) && !empty($value['day']) && !empty($value['year']) && false === checkdate($value['month'], $value['day'], $value['year'])) { + throw new TransformationFailedException('This is an invalid date'); + } + + try { + $dateTime = new \DateTime(sprintf( + '%s-%s-%s %s:%s:%s %s', + empty($value['year']) ? '1970' : $value['year'], + empty($value['month']) ? '1' : $value['month'], + empty($value['day']) ? '1' : $value['day'], + empty($value['hour']) ? '0' : $value['hour'], + empty($value['minute']) ? '0' : $value['minute'], + empty($value['second']) ? '0' : $value['second'], + $this->outputTimezone + )); + + if ($this->inputTimezone !== $this->outputTimezone) { + $dateTime->setTimezone(new \DateTimeZone($this->inputTimezone)); + } + } catch (\Exception $e) { + throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); + } + + return $dateTime; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php new file mode 100644 index 00000000..d755e485 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php @@ -0,0 +1,169 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\Exception\UnexpectedTypeException; + +/** + * Transforms between a normalized time and a localized time string + * + * @author Bernhard Schussek + * @author Florian Eckerstorfer + */ +class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer +{ + private $dateFormat; + private $timeFormat; + private $pattern; + private $calendar; + + /** + * Constructor. + * + * @see BaseDateTimeTransformer::formats for available format options + * + * @param string $inputTimezone The name of the input timezone + * @param string $outputTimezone The name of the output timezone + * @param integer $dateFormat The date format + * @param integer $timeFormat The time format + * @param integer $calendar One of the \IntlDateFormatter calendar constants + * @param string $pattern A pattern to pass to \IntlDateFormatter + * + * @throws UnexpectedTypeException If a format is not supported or if a timezone is not a string + */ + public function __construct($inputTimezone = null, $outputTimezone = null, $dateFormat = null, $timeFormat = null, $calendar = \IntlDateFormatter::GREGORIAN, $pattern = null) + { + parent::__construct($inputTimezone, $outputTimezone); + + if (null === $dateFormat) { + $dateFormat = \IntlDateFormatter::MEDIUM; + } + + if (null === $timeFormat) { + $timeFormat = \IntlDateFormatter::SHORT; + } + + if (!in_array($dateFormat, self::$formats, true)) { + throw new UnexpectedTypeException($dateFormat, implode('", "', self::$formats)); + } + + if (!in_array($timeFormat, self::$formats, true)) { + throw new UnexpectedTypeException($timeFormat, implode('", "', self::$formats)); + } + + $this->dateFormat = $dateFormat; + $this->timeFormat = $timeFormat; + $this->calendar = $calendar; + $this->pattern = $pattern; + } + + /** + * Transforms a normalized date into a localized date string/array. + * + * @param \DateTime $dateTime Normalized date. + * + * @return string|array Localized date string/array. + * + * @throws TransformationFailedException If the given value is not an instance + * of \DateTime or if the date could not + * be transformed. + */ + public function transform($dateTime) + { + if (null === $dateTime) { + return ''; + } + + if (!$dateTime instanceof \DateTime) { + throw new TransformationFailedException('Expected a \DateTime.'); + } + + // convert time to UTC before passing it to the formatter + $dateTime = clone $dateTime; + if ('UTC' !== $this->inputTimezone) { + $dateTime->setTimezone(new \DateTimeZone('UTC')); + } + + $value = $this->getIntlDateFormatter()->format((int) $dateTime->format('U')); + + if (intl_get_error_code() != 0) { + throw new TransformationFailedException(intl_get_error_message()); + } + + return $value; + } + + /** + * Transforms a localized date string/array into a normalized date. + * + * @param string|array $value Localized date string/array + * + * @return \DateTime Normalized date + * + * @throws TransformationFailedException if the given value is not a string, + * if the date could not be parsed or + * if the input timezone is not supported + */ + public function reverseTransform($value) + { + if (!is_string($value)) { + throw new TransformationFailedException('Expected a string.'); + } + + if ('' === $value) { + return null; + } + + $timestamp = $this->getIntlDateFormatter()->parse($value); + + if (intl_get_error_code() != 0) { + throw new TransformationFailedException(intl_get_error_message()); + } + + try { + // read timestamp into DateTime object - the formatter delivers in UTC + $dateTime = new \DateTime(sprintf('@%s UTC', $timestamp)); + } catch (\Exception $e) { + throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); + } + + if ('UTC' !== $this->inputTimezone) { + try { + $dateTime->setTimezone(new \DateTimeZone($this->inputTimezone)); + } catch (\Exception $e) { + throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); + } + } + + return $dateTime; + } + + /** + * Returns a preconfigured IntlDateFormatter instance + * + * @return \IntlDateFormatter + */ + protected function getIntlDateFormatter() + { + $dateFormat = $this->dateFormat; + $timeFormat = $this->timeFormat; + $timezone = $this->outputTimezone; + $calendar = $this->calendar; + $pattern = $this->pattern; + + $intlDateFormatter = new \IntlDateFormatter(\Locale::getDefault(), $dateFormat, $timeFormat, $timezone, $calendar, $pattern); + $intlDateFormatter->setLenient(false); + + return $intlDateFormatter; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToRfc3339Transformer.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToRfc3339Transformer.php new file mode 100644 index 00000000..0eb07422 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToRfc3339Transformer.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * @author Bernhard Schussek + */ +class DateTimeToRfc3339Transformer extends BaseDateTimeTransformer +{ + /** + * {@inheritDoc} + */ + public function transform($dateTime) + { + if (null === $dateTime) { + return ''; + } + + if (!$dateTime instanceof \DateTime) { + throw new TransformationFailedException('Expected a \DateTime.'); + } + + if ($this->inputTimezone !== $this->outputTimezone) { + $dateTime = clone $dateTime; + $dateTime->setTimezone(new \DateTimeZone($this->outputTimezone)); + } + + return preg_replace('/\+00:00$/', 'Z', $dateTime->format('c')); + } + + /** + * {@inheritDoc} + */ + public function reverseTransform($rfc3339) + { + if (!is_string($rfc3339)) { + throw new TransformationFailedException('Expected a string.'); + } + + if ('' === $rfc3339) { + return null; + } + + try { + $dateTime = new \DateTime($rfc3339); + } catch (\Exception $e) { + throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); + } + + if ($this->outputTimezone !== $this->inputTimezone) { + try { + $dateTime->setTimezone(new \DateTimeZone($this->inputTimezone)); + } catch (\Exception $e) { + throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); + } + } + + if (preg_match('/(\d{4})-(\d{2})-(\d{2})/', $rfc3339, $matches)) { + if (!checkdate($matches[2], $matches[3], $matches[1])) { + throw new TransformationFailedException(sprintf( + 'The date "%s-%s-%s" is not a valid date.', + $matches[1], + $matches[2], + $matches[3] + )); + } + } + + return $dateTime; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php new file mode 100644 index 00000000..131f45cb --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php @@ -0,0 +1,231 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\Exception\UnexpectedTypeException; + +/** + * Transforms between a date string and a DateTime object + * + * @author Bernhard Schussek + * @author Florian Eckerstorfer + */ +class DateTimeToStringTransformer extends BaseDateTimeTransformer +{ + /** + * Format used for generating strings + * @var string + */ + private $generateFormat; + + /** + * Format used for parsing strings + * + * Different than the {@link $generateFormat} because formats for parsing + * support additional characters in PHP that are not supported for + * generating strings. + * + * @var string + */ + private $parseFormat; + + /** + * Whether to parse by appending a pipe "|" to the parse format. + * + * This only works as of PHP 5.3.7. + * + * @var Boolean + */ + private $parseUsingPipe; + + /** + * Transforms a \DateTime instance to a string + * + * @see \DateTime::format() for supported formats + * + * @param string $inputTimezone The name of the input timezone + * @param string $outputTimezone The name of the output timezone + * @param string $format The date format + * @param Boolean $parseUsingPipe Whether to parse by appending a pipe "|" to the parse format + * + * @throws UnexpectedTypeException if a timezone is not a string + */ + public function __construct($inputTimezone = null, $outputTimezone = null, $format = 'Y-m-d H:i:s', $parseUsingPipe = null) + { + parent::__construct($inputTimezone, $outputTimezone); + + $this->generateFormat = $this->parseFormat = $format; + + // The pipe in the parser pattern only works as of PHP 5.3.7 + // See http://bugs.php.net/54316 + $this->parseUsingPipe = null === $parseUsingPipe + ? version_compare(phpversion(), '5.3.7', '>=') + : $parseUsingPipe; + + // See http://php.net/manual/en/datetime.createfromformat.php + // The character "|" in the format makes sure that the parts of a date + // that are *not* specified in the format are reset to the corresponding + // values from 1970-01-01 00:00:00 instead of the current time. + // Without "|" and "Y-m-d", "2010-02-03" becomes "2010-02-03 12:32:47", + // where the time corresponds to the current server time. + // With "|" and "Y-m-d", "2010-02-03" becomes "2010-02-03 00:00:00", + // which is at least deterministic and thus used here. + if ($this->parseUsingPipe && false === strpos($this->parseFormat, '|')) { + $this->parseFormat .= '|'; + } + } + + /** + * Transforms a DateTime object into a date string with the configured format + * and timezone + * + * @param \DateTime $value A DateTime object + * + * @return string A value as produced by PHP's date() function + * + * @throws TransformationFailedException If the given value is not a \DateTime + * instance or if the output timezone + * is not supported. + */ + public function transform($value) + { + if (null === $value) { + return ''; + } + + if (!$value instanceof \DateTime) { + throw new TransformationFailedException('Expected a \DateTime.'); + } + + $value = clone $value; + try { + $value->setTimezone(new \DateTimeZone($this->outputTimezone)); + } catch (\Exception $e) { + throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); + } + + return $value->format($this->generateFormat); + } + + /** + * Transforms a date string in the configured timezone into a DateTime object. + * + * @param string $value A value as produced by PHP's date() function + * + * @return \DateTime An instance of \DateTime + * + * @throws TransformationFailedException If the given value is not a string, + * if the date could not be parsed or + * if the input timezone is not supported. + */ + public function reverseTransform($value) + { + if (empty($value)) { + return null; + } + + if (!is_string($value)) { + throw new TransformationFailedException('Expected a string.'); + } + + try { + $outputTz = new \DateTimeZone($this->outputTimezone); + $dateTime = \DateTime::createFromFormat($this->parseFormat, $value, $outputTz); + + $lastErrors = \DateTime::getLastErrors(); + + if (0 < $lastErrors['warning_count'] || 0 < $lastErrors['error_count']) { + throw new TransformationFailedException( + implode(', ', array_merge( + array_values($lastErrors['warnings']), + array_values($lastErrors['errors']) + )) + ); + } + + // On PHP versions < 5.3.7 we need to emulate the pipe operator + // and reset parts not given in the format to their equivalent + // of the UNIX base timestamp. + if (!$this->parseUsingPipe) { + list($year, $month, $day, $hour, $minute, $second) = explode('-', $dateTime->format('Y-m-d-H-i-s')); + + // Check which of the date parts are present in the pattern + preg_match( + '/(' . + '(?P[djDl])|' . + '(?P[FMmn])|' . + '(?P[Yy])|' . + '(?P[ghGH])|' . + '(?Pi)|' . + '(?Ps)|' . + '(?Pz)|' . + '(?PU)|' . + '[^djDlFMmnYyghGHiszU]' . + ')*/', + $this->parseFormat, + $matches + ); + + // preg_match() does not guarantee to set all indices, so + // set them unless given + $matches = array_merge(array( + 'day' => false, + 'month' => false, + 'year' => false, + 'hour' => false, + 'minute' => false, + 'second' => false, + 'dayofyear' => false, + 'timestamp' => false, + ), $matches); + + // Reset all parts that don't exist in the format to the + // corresponding part of the UNIX base timestamp + if (!$matches['timestamp']) { + if (!$matches['dayofyear']) { + if (!$matches['day']) { + $day = 1; + } + if (!$matches['month']) { + $month = 1; + } + } + if (!$matches['year']) { + $year = 1970; + } + if (!$matches['hour']) { + $hour = 0; + } + if (!$matches['minute']) { + $minute = 0; + } + if (!$matches['second']) { + $second = 0; + } + $dateTime->setDate($year, $month, $day); + $dateTime->setTime($hour, $minute, $second); + } + } + + if ($this->inputTimezone !== $this->outputTimezone) { + $dateTime->setTimeZone(new \DateTimeZone($this->inputTimezone)); + } + } catch (TransformationFailedException $e) { + throw $e; + } catch (\Exception $e) { + throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); + } + + return $dateTime; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToTimestampTransformer.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToTimestampTransformer.php new file mode 100644 index 00000000..d2ca6604 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToTimestampTransformer.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * Transforms between a timestamp and a DateTime object + * + * @author Bernhard Schussek + * @author Florian Eckerstorfer + */ +class DateTimeToTimestampTransformer extends BaseDateTimeTransformer +{ + /** + * Transforms a DateTime object into a timestamp in the configured timezone. + * + * @param \DateTime $value A \DateTime object + * + * @return integer A timestamp + * + * @throws TransformationFailedException If the given value is not an instance + * of \DateTime or if the output + * timezone is not supported. + */ + public function transform($value) + { + if (null === $value) { + return null; + } + + if (!$value instanceof \DateTime) { + throw new TransformationFailedException('Expected a \DateTime.'); + } + + $value = clone $value; + try { + $value->setTimezone(new \DateTimeZone($this->outputTimezone)); + } catch (\Exception $e) { + throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); + } + + return (int) $value->format('U'); + } + + /** + * Transforms a timestamp in the configured timezone into a DateTime object + * + * @param string $value A timestamp + * + * @return \DateTime A \DateTime object + * + * @throws TransformationFailedException If the given value is not a timestamp + * or if the given timestamp is invalid. + */ + public function reverseTransform($value) + { + if (null === $value) { + return null; + } + + if (!is_numeric($value)) { + throw new TransformationFailedException('Expected a numeric.'); + } + + try { + $dateTime = new \DateTime(); + $dateTime->setTimezone(new \DateTimeZone($this->outputTimezone)); + $dateTime->setTimestamp($value); + + if ($this->inputTimezone !== $this->outputTimezone) { + $dateTime->setTimezone(new \DateTimeZone($this->inputTimezone)); + } + } catch (\Exception $e) { + throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); + } + + return $dateTime; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php new file mode 100644 index 00000000..6bb48a9a --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * Transforms between an integer and a localized number with grouping + * (each thousand) and comma separators. + * + * @author Bernhard Schussek + */ +class IntegerToLocalizedStringTransformer extends NumberToLocalizedStringTransformer +{ + /** + * {@inheritDoc} + */ + public function reverseTransform($value) + { + if (!is_string($value)) { + throw new TransformationFailedException('Expected a string.'); + } + + if ('' === $value) { + return null; + } + + if ('NaN' === $value) { + throw new TransformationFailedException('"NaN" is not a valid integer'); + } + + $formatter = $this->getNumberFormatter(); + $value = $formatter->parse( + $value, + PHP_INT_SIZE == 8 ? $formatter::TYPE_INT64 : $formatter::TYPE_INT32 + ); + + if (intl_is_failure($formatter->getErrorCode())) { + throw new TransformationFailedException($formatter->getErrorMessage()); + } + + return $value; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php new file mode 100644 index 00000000..5b8e9d96 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * Transforms between a normalized format and a localized money string. + * + * @author Bernhard Schussek + * @author Florian Eckerstorfer + */ +class MoneyToLocalizedStringTransformer extends NumberToLocalizedStringTransformer +{ + + private $divisor; + + public function __construct($precision = null, $grouping = null, $roundingMode = null, $divisor = null) + { + if (null === $grouping) { + $grouping = true; + } + + if (null === $precision) { + $precision = 2; + } + + parent::__construct($precision, $grouping, $roundingMode); + + if (null === $divisor) { + $divisor = 1; + } + + $this->divisor = $divisor; + } + + /** + * Transforms a normalized format into a localized money string. + * + * @param number $value Normalized number + * + * @return string Localized money string. + * + * @throws TransformationFailedException If the given value is not numeric or + * if the value can not be transformed. + */ + public function transform($value) + { + if (null !== $value) { + if (!is_numeric($value)) { + throw new TransformationFailedException('Expected a numeric.'); + } + + $value /= $this->divisor; + } + + return parent::transform($value); + } + + /** + * Transforms a localized money string into a normalized format. + * + * @param string $value Localized money string + * + * @return number Normalized number + * + * @throws TransformationFailedException If the given value is not a string + * or if the value can not be transformed. + */ + public function reverseTransform($value) + { + $value = parent::reverseTransform($value); + + if (null !== $value) { + $value *= $this->divisor; + } + + return $value; + } + +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php new file mode 100644 index 00000000..b0c59b3e --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php @@ -0,0 +1,184 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * Transforms between a number type and a localized number with grouping + * (each thousand) and comma separators. + * + * @author Bernhard Schussek + * @author Florian Eckerstorfer + */ +class NumberToLocalizedStringTransformer implements DataTransformerInterface +{ + const ROUND_FLOOR = \NumberFormatter::ROUND_FLOOR; + const ROUND_DOWN = \NumberFormatter::ROUND_DOWN; + const ROUND_HALFDOWN = \NumberFormatter::ROUND_HALFDOWN; + const ROUND_HALFEVEN = \NumberFormatter::ROUND_HALFEVEN; + const ROUND_HALFUP = \NumberFormatter::ROUND_HALFUP; + const ROUND_UP = \NumberFormatter::ROUND_UP; + const ROUND_CEILING = \NumberFormatter::ROUND_CEILING; + + protected $precision; + + protected $grouping; + + protected $roundingMode; + + public function __construct($precision = null, $grouping = null, $roundingMode = null) + { + if (null === $grouping) { + $grouping = false; + } + + if (null === $roundingMode) { + $roundingMode = self::ROUND_HALFUP; + } + + $this->precision = $precision; + $this->grouping = $grouping; + $this->roundingMode = $roundingMode; + } + + /** + * Transforms a number type into localized number. + * + * @param integer|float $value Number value. + * + * @return string Localized value. + * + * @throws TransformationFailedException If the given value is not numeric + * or if the value can not be transformed. + */ + public function transform($value) + { + if (null === $value) { + return ''; + } + + if (!is_numeric($value)) { + throw new TransformationFailedException('Expected a numeric.'); + } + + $formatter = $this->getNumberFormatter(); + $value = $formatter->format($value); + + if (intl_is_failure($formatter->getErrorCode())) { + throw new TransformationFailedException($formatter->getErrorMessage()); + } + + // Convert fixed spaces to normal ones + $value = str_replace("\xc2\xa0", ' ', $value); + + return $value; + } + + /** + * Transforms a localized number into an integer or float + * + * @param string $value The localized value + * + * @return integer|float The numeric value + * + * @throws TransformationFailedException If the given value is not a string + * or if the value can not be transformed. + */ + public function reverseTransform($value) + { + if (!is_string($value)) { + throw new TransformationFailedException('Expected a string.'); + } + + if ('' === $value) { + return null; + } + + if ('NaN' === $value) { + throw new TransformationFailedException('"NaN" is not a valid number'); + } + + $position = 0; + $formatter = $this->getNumberFormatter(); + $groupSep = $formatter->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL); + $decSep = $formatter->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL); + + if ('.' !== $decSep && (!$this->grouping || '.' !== $groupSep)) { + $value = str_replace('.', $decSep, $value); + } + + if (',' !== $decSep && (!$this->grouping || ',' !== $groupSep)) { + $value = str_replace(',', $decSep, $value); + } + + $result = $formatter->parse($value, \NumberFormatter::TYPE_DOUBLE, $position); + + if (intl_is_failure($formatter->getErrorCode())) { + throw new TransformationFailedException($formatter->getErrorMessage()); + } + + if ($result >= PHP_INT_MAX || $result <= -PHP_INT_MAX) { + throw new TransformationFailedException('I don\'t have a clear idea what infinity looks like'); + } + + if (function_exists('mb_detect_encoding') && false !== $encoding = mb_detect_encoding($value)) { + $strlen = function ($string) use ($encoding) { + return mb_strlen($string, $encoding); + }; + $substr = function ($string, $offset, $length) use ($encoding) { + return mb_substr($string, $offset, $length, $encoding); + }; + } else { + $strlen = 'strlen'; + $substr = 'substr'; + } + + $length = $strlen($value); + + // After parsing, position holds the index of the character where the + // parsing stopped + if ($position < $length) { + // Check if there are unrecognized characters at the end of the + // number (excluding whitespace characters) + $remainder = trim($substr($value, $position, $length), " \t\n\r\0\x0b\xc2\xa0"); + + if ('' !== $remainder) { + throw new TransformationFailedException( + sprintf('The number contains unrecognized characters: "%s"', $remainder) + ); + } + } + + return $result; + } + + /** + * Returns a preconfigured \NumberFormatter instance + * + * @return \NumberFormatter + */ + protected function getNumberFormatter() + { + $formatter = new \NumberFormatter(\Locale::getDefault(), \NumberFormatter::DECIMAL); + + if (null !== $this->precision) { + $formatter->setAttribute(\NumberFormatter::FRACTION_DIGITS, $this->precision); + $formatter->setAttribute(\NumberFormatter::ROUNDING_MODE, $this->roundingMode); + } + + $formatter->setAttribute(\NumberFormatter::GROUPING_USED, $this->grouping); + + return $formatter; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php new file mode 100644 index 00000000..e099d436 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php @@ -0,0 +1,149 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\Exception\UnexpectedTypeException; + +/** + * Transforms between a normalized format (integer or float) and a percentage value. + * + * @author Bernhard Schussek + * @author Florian Eckerstorfer + */ +class PercentToLocalizedStringTransformer implements DataTransformerInterface +{ + const FRACTIONAL = 'fractional'; + const INTEGER = 'integer'; + + protected static $types = array( + self::FRACTIONAL, + self::INTEGER, + ); + + private $type; + + private $precision; + + /** + * Constructor. + * + * @see self::$types for a list of supported types + * + * @param integer $precision The precision + * @param string $type One of the supported types + * + * @throws UnexpectedTypeException if the given value of type is unknown + */ + public function __construct($precision = null, $type = null) + { + if (null === $precision) { + $precision = 0; + } + + if (null === $type) { + $type = self::FRACTIONAL; + } + + if (!in_array($type, self::$types, true)) { + throw new UnexpectedTypeException($type, implode('", "', self::$types)); + } + + $this->type = $type; + $this->precision = $precision; + } + + /** + * Transforms between a normalized format (integer or float) into a percentage value. + * + * @param number $value Normalized value + * + * @return number Percentage value + * + * @throws TransformationFailedException If the given value is not numeric or + * if the value could not be transformed. + */ + public function transform($value) + { + if (null === $value) { + return ''; + } + + if (!is_numeric($value)) { + throw new TransformationFailedException('Expected a numeric.'); + } + + if (self::FRACTIONAL == $this->type) { + $value *= 100; + } + + $formatter = $this->getNumberFormatter(); + $value = $formatter->format($value); + + if (intl_is_failure($formatter->getErrorCode())) { + throw new TransformationFailedException($formatter->getErrorMessage()); + } + + // replace the UTF-8 non break spaces + return $value; + } + + /** + * Transforms between a percentage value into a normalized format (integer or float). + * + * @param number $value Percentage value. + * + * @return number Normalized value. + * + * @throws TransformationFailedException If the given value is not a string or + * if the value could not be transformed. + */ + public function reverseTransform($value) + { + if (!is_string($value)) { + throw new TransformationFailedException('Expected a string.'); + } + + if ('' === $value) { + return null; + } + + $formatter = $this->getNumberFormatter(); + // replace normal spaces so that the formatter can read them + $value = $formatter->parse(str_replace(' ', ' ', $value)); + + if (intl_is_failure($formatter->getErrorCode())) { + throw new TransformationFailedException($formatter->getErrorMessage()); + } + + if (self::FRACTIONAL == $this->type) { + $value /= 100; + } + + return $value; + } + + /** + * Returns a preconfigured \NumberFormatter instance + * + * @return \NumberFormatter + */ + protected function getNumberFormatter() + { + $formatter = new \NumberFormatter(\Locale::getDefault(), \NumberFormatter::DECIMAL); + + $formatter->setAttribute(\NumberFormatter::FRACTION_DIGITS, $this->precision); + + return $formatter; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ValueToDuplicatesTransformer.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ValueToDuplicatesTransformer.php new file mode 100644 index 00000000..c34a0139 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ValueToDuplicatesTransformer.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * @author Bernhard Schussek + */ +class ValueToDuplicatesTransformer implements DataTransformerInterface +{ + private $keys; + + public function __construct(array $keys) + { + $this->keys = $keys; + } + + /** + * Duplicates the given value through the array. + * + * @param mixed $value The value + * + * @return array The array + */ + public function transform($value) + { + $result = array(); + + foreach ($this->keys as $key) { + $result[$key] = $value; + } + + return $result; + } + + /** + * Extracts the duplicated value from an array. + * + * @param array $array + * + * @return mixed The value + * + * @throws TransformationFailedException If the given value is not an array or + * if the given array can not be transformed. + */ + public function reverseTransform($array) + { + if (!is_array($array)) { + throw new TransformationFailedException('Expected an array.'); + } + + $result = current($array); + $emptyKeys = array(); + + foreach ($this->keys as $key) { + if (!empty($array[$key])) { + if ($array[$key] !== $result) { + throw new TransformationFailedException( + 'All values in the array should be the same' + ); + } + } else { + $emptyKeys[] = $key; + } + } + + if (count($emptyKeys) > 0) { + if (count($emptyKeys) == count($this->keys)) { + // All keys empty + return null; + } + + throw new TransformationFailedException( + sprintf('The keys "%s" should not be empty', implode('", "', $emptyKeys) + )); + } + + return $result; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/FixCheckboxInputListener.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/FixCheckboxInputListener.php new file mode 100644 index 00000000..1f62e060 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/FixCheckboxInputListener.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\EventListener; + +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface; + +/** + * Takes care of converting the input from a list of checkboxes to a correctly + * indexed array. + * + * @author Bernhard Schussek + */ +class FixCheckboxInputListener implements EventSubscriberInterface +{ + private $choiceList; + + /** + * Constructor. + * + * @param ChoiceListInterface $choiceList + */ + public function __construct(ChoiceListInterface $choiceList) + { + $this->choiceList = $choiceList; + } + + public function preSubmit(FormEvent $event) + { + $values = (array) $event->getData(); + $indices = $this->choiceList->getIndicesForValues($values); + + $event->setData(count($indices) > 0 ? array_combine($indices, $values) : array()); + } + + /** + * Alias of {@link preSubmit()}. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link preSubmit()} instead. + */ + public function preBind(FormEvent $event) + { + $this->preSubmit($event); + } + + public static function getSubscribedEvents() + { + return array(FormEvents::PRE_SUBMIT => 'preSubmit'); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/FixRadioInputListener.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/FixRadioInputListener.php new file mode 100644 index 00000000..bf03792f --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/FixRadioInputListener.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\EventListener; + +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface; + +/** + * Takes care of converting the input from a single radio button + * to an array. + * + * @author Bernhard Schussek + */ +class FixRadioInputListener implements EventSubscriberInterface +{ + private $choiceList; + + private $placeholderPresent; + + /** + * Constructor. + * + * @param ChoiceListInterface $choiceList + * @param Boolean $placeholderPresent + */ + public function __construct(ChoiceListInterface $choiceList, $placeholderPresent) + { + $this->choiceList = $choiceList; + $this->placeholderPresent = $placeholderPresent; + } + + public function preSubmit(FormEvent $event) + { + $value = $event->getData(); + $index = current($this->choiceList->getIndicesForValues(array($value))); + + $event->setData(false !== $index ? array($index => $value) : ($this->placeholderPresent ? array('placeholder' => '') : array())) ; + } + + /** + * Alias of {@link preSubmit()}. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link preSubmit()} instead. + */ + public function preBind(FormEvent $event) + { + $this->preSubmit($event); + } + + public static function getSubscribedEvents() + { + return array(FormEvents::PRE_SUBMIT => 'preSubmit'); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/FixUrlProtocolListener.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/FixUrlProtocolListener.php new file mode 100644 index 00000000..e25dacf2 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/FixUrlProtocolListener.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\EventListener; + +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Adds a protocol to a URL if it doesn't already have one. + * + * @author Bernhard Schussek + */ +class FixUrlProtocolListener implements EventSubscriberInterface +{ + private $defaultProtocol; + + public function __construct($defaultProtocol = 'http') + { + $this->defaultProtocol = $defaultProtocol; + } + + public function onSubmit(FormEvent $event) + { + $data = $event->getData(); + + if ($this->defaultProtocol && $data && !preg_match('~^\w+://~', $data)) { + $event->setData($this->defaultProtocol.'://'.$data); + } + } + + /** + * Alias of {@link onSubmit()}. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link onSubmit()} instead. + */ + public function onBind(FormEvent $event) + { + $this->onSubmit($event); + } + + public static function getSubscribedEvents() + { + return array(FormEvents::SUBMIT => 'onSubmit'); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/MergeCollectionListener.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/MergeCollectionListener.php new file mode 100644 index 00000000..4d0bdfaa --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/MergeCollectionListener.php @@ -0,0 +1,137 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\EventListener; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Exception\UnexpectedTypeException; + +/** + * @author Bernhard Schussek + */ +class MergeCollectionListener implements EventSubscriberInterface +{ + /** + * Whether elements may be added to the collection + * @var Boolean + */ + private $allowAdd; + + /** + * Whether elements may be removed from the collection + * @var Boolean + */ + private $allowDelete; + + /** + * Creates a new listener. + * + * @param Boolean $allowAdd Whether values might be added to the + * collection. + * @param Boolean $allowDelete Whether values might be removed from the + * collection. + */ + public function __construct($allowAdd = false, $allowDelete = false) + { + $this->allowAdd = $allowAdd; + $this->allowDelete = $allowDelete; + } + + public static function getSubscribedEvents() + { + return array( + FormEvents::SUBMIT => 'onSubmit', + ); + } + + public function onSubmit(FormEvent $event) + { + $dataToMergeInto = $event->getForm()->getNormData(); + $data = $event->getData(); + + if (null === $data) { + $data = array(); + } + + if (!is_array($data) && !($data instanceof \Traversable && $data instanceof \ArrayAccess)) { + throw new UnexpectedTypeException($data, 'array or (\Traversable and \ArrayAccess)'); + } + + if (null !== $dataToMergeInto && !is_array($dataToMergeInto) && !($dataToMergeInto instanceof \Traversable && $dataToMergeInto instanceof \ArrayAccess)) { + throw new UnexpectedTypeException($dataToMergeInto, 'array or (\Traversable and \ArrayAccess)'); + } + + // If we are not allowed to change anything, return immediately + if ((!$this->allowAdd && !$this->allowDelete) || $data === $dataToMergeInto) { + $event->setData($dataToMergeInto); + + return; + } + + if (!$dataToMergeInto) { + // No original data was set. Set it if allowed + if ($this->allowAdd) { + $dataToMergeInto = $data; + } + } else { + // Calculate delta + $itemsToAdd = is_object($data) ? clone $data : $data; + $itemsToDelete = array(); + + foreach ($dataToMergeInto as $beforeKey => $beforeItem) { + foreach ($data as $afterKey => $afterItem) { + if ($afterItem === $beforeItem) { + // Item found, next original item + unset($itemsToAdd[$afterKey]); + continue 2; + } + } + + // Item not found, remember for deletion + $itemsToDelete[] = $beforeKey; + } + + // Remove deleted items before adding to free keys that are to be + // replaced + if ($this->allowDelete) { + foreach ($itemsToDelete as $key) { + unset($dataToMergeInto[$key]); + } + } + + // Add remaining items + if ($this->allowAdd) { + foreach ($itemsToAdd as $key => $item) { + if (!isset($dataToMergeInto[$key])) { + $dataToMergeInto[$key] = $item; + } else { + $dataToMergeInto[] = $item; + } + } + } + } + + $event->setData($dataToMergeInto); + } + + /** + * Alias of {@link onSubmit()}. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link onSubmit()} instead. + */ + public function onBind(FormEvent $event) + { + $this->onSubmit($event); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/ResizeFormListener.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/ResizeFormListener.php new file mode 100644 index 00000000..f1c39db2 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/ResizeFormListener.php @@ -0,0 +1,173 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\EventListener; + +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Resize a collection form element based on the data sent from the client. + * + * @author Bernhard Schussek + */ +class ResizeFormListener implements EventSubscriberInterface +{ + /** + * @var string + */ + protected $type; + + /** + * @var array + */ + protected $options; + + /** + * Whether children could be added to the group + * @var Boolean + */ + protected $allowAdd; + + /** + * Whether children could be removed from the group + * @var Boolean + */ + protected $allowDelete; + + public function __construct($type, array $options = array(), $allowAdd = false, $allowDelete = false) + { + $this->type = $type; + $this->allowAdd = $allowAdd; + $this->allowDelete = $allowDelete; + $this->options = $options; + } + + public static function getSubscribedEvents() + { + return array( + FormEvents::PRE_SET_DATA => 'preSetData', + FormEvents::PRE_SUBMIT => 'preSubmit', + // (MergeCollectionListener, MergeDoctrineCollectionListener) + FormEvents::SUBMIT => array('onSubmit', 50), + ); + } + + public function preSetData(FormEvent $event) + { + $form = $event->getForm(); + $data = $event->getData(); + + if (null === $data) { + $data = array(); + } + + if (!is_array($data) && !($data instanceof \Traversable && $data instanceof \ArrayAccess)) { + throw new UnexpectedTypeException($data, 'array or (\Traversable and \ArrayAccess)'); + } + + // First remove all rows + foreach ($form as $name => $child) { + $form->remove($name); + } + + // Then add all rows again in the correct order + foreach ($data as $name => $value) { + $form->add($name, $this->type, array_replace(array( + 'property_path' => '['.$name.']', + ), $this->options)); + } + } + + public function preSubmit(FormEvent $event) + { + $form = $event->getForm(); + $data = $event->getData(); + + if (null === $data || '' === $data) { + $data = array(); + } + + if (!is_array($data) && !($data instanceof \Traversable && $data instanceof \ArrayAccess)) { + throw new UnexpectedTypeException($data, 'array or (\Traversable and \ArrayAccess)'); + } + + // Remove all empty rows + if ($this->allowDelete) { + foreach ($form as $name => $child) { + if (!isset($data[$name])) { + $form->remove($name); + } + } + } + + // Add all additional rows + if ($this->allowAdd) { + foreach ($data as $name => $value) { + if (!$form->has($name)) { + $form->add($name, $this->type, array_replace(array( + 'property_path' => '['.$name.']', + ), $this->options)); + } + } + } + } + + public function onSubmit(FormEvent $event) + { + $form = $event->getForm(); + $data = $event->getData(); + + if (null === $data) { + $data = array(); + } + + if (!is_array($data) && !($data instanceof \Traversable && $data instanceof \ArrayAccess)) { + throw new UnexpectedTypeException($data, 'array or (\Traversable and \ArrayAccess)'); + } + + // The data mapper only adds, but does not remove items, so do this + // here + if ($this->allowDelete) { + foreach ($data as $name => $child) { + if (!$form->has($name)) { + unset($data[$name]); + } + } + } + + $event->setData($data); + } + + /** + * Alias of {@link preSubmit()}. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link preSubmit()} instead. + */ + public function preBind(FormEvent $event) + { + $this->preSubmit($event); + } + + /** + * Alias of {@link onSubmit()}. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link onSubmit()} instead. + */ + public function onBind(FormEvent $event) + { + $this->onSubmit($event); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/TrimListener.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/TrimListener.php new file mode 100644 index 00000000..cbe6e0ab --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/TrimListener.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\EventListener; + +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Trims string data + * + * @author Bernhard Schussek + */ +class TrimListener implements EventSubscriberInterface +{ + public function preSubmit(FormEvent $event) + { + $data = $event->getData(); + + if (!is_string($data)) { + return; + } + + if (null !== $result = @preg_replace('/^[\pZ\p{Cc}]+|[\pZ\p{Cc}]+$/u', '', $data)) { + $event->setData($result); + } else { + $event->setData(trim($data)); + } + } + + /** + * Alias of {@link preSubmit()}. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link preSubmit()} instead. + */ + public function preBind(FormEvent $event) + { + $this->preSubmit($event); + } + + public static function getSubscribedEvents() + { + return array(FormEvents::PRE_SUBMIT => 'preSubmit'); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/BaseType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/BaseType.php new file mode 100644 index 00000000..79333a67 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/BaseType.php @@ -0,0 +1,121 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +/** + * Encapsulates common logic of {@link FormType} and {@link ButtonType}. + * + * This type does not appear in the form's type inheritance chain and as such + * cannot be extended (via {@link FormTypeExtension}s) nor themed. + * + * @author Bernhard Schussek + */ +abstract class BaseType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->setDisabled($options['disabled']); + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $name = $form->getName(); + $blockName = $options['block_name'] ?: $form->getName(); + $translationDomain = $options['translation_domain']; + + if ($view->parent) { + if ('' !== ($parentFullName = $view->parent->vars['full_name'])) { + $id = sprintf('%s_%s', $view->parent->vars['id'], $name); + $fullName = sprintf('%s[%s]', $parentFullName, $name); + $uniqueBlockPrefix = sprintf('%s_%s', $view->parent->vars['unique_block_prefix'], $blockName); + } else { + $id = $name; + $fullName = $name; + $uniqueBlockPrefix = '_'.$blockName; + } + + if (!$translationDomain) { + $translationDomain = $view->parent->vars['translation_domain']; + } + } else { + $id = $name; + $fullName = $name; + $uniqueBlockPrefix = '_'.$blockName; + + // Strip leading underscores and digits. These are allowed in + // form names, but not in HTML4 ID attributes. + // http://www.w3.org/TR/html401/struct/global.html#adef-id + $id = ltrim($id, '_0123456789'); + } + + $blockPrefixes = array(); + for ($type = $form->getConfig()->getType(); null !== $type; $type = $type->getParent()) { + array_unshift($blockPrefixes, $type->getName()); + } + $blockPrefixes[] = $uniqueBlockPrefix; + + if (!$translationDomain) { + $translationDomain = 'messages'; + } + + $view->vars = array_replace($view->vars, array( + 'form' => $view, + 'id' => $id, + 'name' => $name, + 'full_name' => $fullName, + 'disabled' => $form->isDisabled(), + 'label' => $options['label'], + 'multipart' => false, + 'attr' => $options['attr'], + 'block_prefixes' => $blockPrefixes, + 'unique_block_prefix' => $uniqueBlockPrefix, + 'translation_domain' => $translationDomain, + // Using the block name here speeds up performance in collection + // forms, where each entry has the same full block name. + // Including the type is important too, because if rows of a + // collection form have different types (dynamically), they should + // be rendered differently. + // https://github.com/symfony/symfony/issues/5038 + 'cache_key' => $uniqueBlockPrefix.'_'.$form->getConfig()->getType()->getName(), + )); + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'block_name' => null, + 'disabled' => false, + 'label' => null, + 'attr' => array(), + 'translation_domain' => null, + )); + + $resolver->setAllowedTypes(array( + 'attr' => 'array', + )); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/BirthdayType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/BirthdayType.php new file mode 100644 index 00000000..5314c140 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/BirthdayType.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class BirthdayType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'years' => range(date('Y') - 120, date('Y')), + )); + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'date'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'birthday'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/ButtonType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/ButtonType.php new file mode 100644 index 00000000..3569963b --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/ButtonType.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\ButtonTypeInterface; + +/** + * A form button. + * + * @author Bernhard Schussek + */ +class ButtonType extends BaseType implements ButtonTypeInterface +{ + /** + * {@inheritdoc} + */ + public function getParent() + { + return null; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'button'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php new file mode 100644 index 00000000..214e581a --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\Extension\Core\DataTransformer\BooleanToStringTransformer; +use Symfony\Component\Form\FormView; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class CheckboxType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder + ->addViewTransformer(new BooleanToStringTransformer($options['value'])) + ; + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $view->vars = array_replace($view->vars, array( + 'value' => $options['value'], + 'checked' => null !== $form->getViewData(), + )); + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $emptyData = function (FormInterface $form, $clientData) { + return $clientData; + }; + + $resolver->setDefaults(array( + 'value' => '1', + 'empty_data' => $emptyData, + 'compound' => false, + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'checkbox'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php new file mode 100644 index 00000000..9a3fdef1 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -0,0 +1,274 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\View\ChoiceView; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\Exception\LogicException; +use Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList; +use Symfony\Component\Form\Extension\Core\EventListener\FixRadioInputListener; +use Symfony\Component\Form\Extension\Core\EventListener\FixCheckboxInputListener; +use Symfony\Component\Form\Extension\Core\EventListener\MergeCollectionListener; +use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToValueTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToBooleanArrayTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToValuesTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToBooleanArrayTransformer; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class ChoiceType extends AbstractType +{ + /** + * Caches created choice lists. + * @var array + */ + private $choiceListCache = array(); + + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + if (!$options['choice_list'] && !is_array($options['choices']) && !$options['choices'] instanceof \Traversable) { + throw new LogicException('Either the option "choices" or "choice_list" must be set.'); + } + + if ($options['expanded']) { + // Initialize all choices before doing the index check below. + // This helps in cases where index checks are optimized for non + // initialized choice lists. For example, when using an SQL driver, + // the index check would read in one SQL query and the initialization + // requires another SQL query. When the initialization is done first, + // one SQL query is sufficient. + $preferredViews = $options['choice_list']->getPreferredViews(); + $remainingViews = $options['choice_list']->getRemainingViews(); + + // Check if the choices already contain the empty value + // Only add the empty value option if this is not the case + if (null !== $options['empty_value'] && 0 === count($options['choice_list']->getIndicesForValues(array('')))) { + $placeholderView = new ChoiceView(null, '', $options['empty_value']); + + // "placeholder" is a reserved index + // see also ChoiceListInterface::getIndicesForChoices() + $this->addSubForms($builder, array('placeholder' => $placeholderView), $options); + } + + $this->addSubForms($builder, $preferredViews, $options); + $this->addSubForms($builder, $remainingViews, $options); + + if ($options['multiple']) { + $builder->addViewTransformer(new ChoicesToBooleanArrayTransformer($options['choice_list'])); + $builder->addEventSubscriber(new FixCheckboxInputListener($options['choice_list']), 10); + } else { + $builder->addViewTransformer(new ChoiceToBooleanArrayTransformer($options['choice_list'], $builder->has('placeholder'))); + $builder->addEventSubscriber(new FixRadioInputListener($options['choice_list'], $builder->has('placeholder')), 10); + } + } else { + if ($options['multiple']) { + $builder->addViewTransformer(new ChoicesToValuesTransformer($options['choice_list'])); + } else { + $builder->addViewTransformer(new ChoiceToValueTransformer($options['choice_list'])); + } + } + + if ($options['multiple'] && $options['by_reference']) { + // Make sure the collection created during the client->norm + // transformation is merged back into the original collection + $builder->addEventSubscriber(new MergeCollectionListener(true, true)); + } + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $view->vars = array_replace($view->vars, array( + 'multiple' => $options['multiple'], + 'expanded' => $options['expanded'], + 'preferred_choices' => $options['choice_list']->getPreferredViews(), + 'choices' => $options['choice_list']->getRemainingViews(), + 'separator' => '-------------------', + 'empty_value' => null, + )); + + // The decision, whether a choice is selected, is potentially done + // thousand of times during the rendering of a template. Provide a + // closure here that is optimized for the value of the form, to + // avoid making the type check inside the closure. + if ($options['multiple']) { + $view->vars['is_selected'] = function ($choice, array $values) { + return false !== array_search($choice, $values, true); + }; + } else { + $view->vars['is_selected'] = function ($choice, $value) { + return $choice === $value; + }; + } + + // Check if the choices already contain the empty value + // Only add the empty value option if this is not the case + if (null !== $options['empty_value'] && 0 === count($options['choice_list']->getIndicesForValues(array('')))) { + $view->vars['empty_value'] = $options['empty_value']; + } + + if ($options['multiple'] && !$options['expanded']) { + // Add "[]" to the name in case a select tag with multiple options is + // displayed. Otherwise only one of the selected options is sent in the + // POST request. + $view->vars['full_name'] = $view->vars['full_name'].'[]'; + } + } + + /** + * {@inheritdoc} + */ + public function finishView(FormView $view, FormInterface $form, array $options) + { + if ($options['expanded']) { + // Radio buttons should have the same name as the parent + $childName = $view->vars['full_name']; + + // Checkboxes should append "[]" to allow multiple selection + if ($options['multiple']) { + $childName .= '[]'; + } + + foreach ($view as $childView) { + $childView->vars['full_name'] = $childName; + } + } + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $choiceListCache =& $this->choiceListCache; + + $choiceList = function (Options $options) use (&$choiceListCache) { + // Harden against NULL values (like in EntityType and ModelType) + $choices = null !== $options['choices'] ? $options['choices'] : array(); + + // Reuse existing choice lists in order to increase performance + $hash = md5(json_encode(array($choices, $options['preferred_choices']))); + + if (!isset($choiceListCache[$hash])) { + $choiceListCache[$hash] = new SimpleChoiceList($choices, $options['preferred_choices']); + } + + return $choiceListCache[$hash]; + }; + + $emptyData = function (Options $options) { + if ($options['multiple'] || $options['expanded']) { + return array(); + } + + return ''; + }; + + $emptyValue = function (Options $options) { + return $options['required'] ? null : ''; + }; + + $emptyValueNormalizer = function (Options $options, $emptyValue) { + if ($options['multiple']) { + // never use an empty value for this case + return null; + } elseif (false === $emptyValue) { + // an empty value should be added but the user decided otherwise + return null; + } elseif ($options['expanded'] && '' === $emptyValue) { + // never use an empty label for radio buttons + return 'None'; + } + + // empty value has been set explicitly + return $emptyValue; + }; + + $compound = function (Options $options) { + return $options['expanded']; + }; + + $resolver->setDefaults(array( + 'multiple' => false, + 'expanded' => false, + 'choice_list' => $choiceList, + 'choices' => array(), + 'preferred_choices' => array(), + 'empty_data' => $emptyData, + 'empty_value' => $emptyValue, + 'error_bubbling' => false, + 'compound' => $compound, + // The view data is always a string, even if the "data" option + // is manually set to an object. + // See https://github.com/symfony/symfony/pull/5582 + 'data_class' => null, + )); + + $resolver->setNormalizers(array( + 'empty_value' => $emptyValueNormalizer, + )); + + $resolver->setAllowedTypes(array( + 'choice_list' => array('null', 'Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface'), + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'choice'; + } + + /** + * Adds the sub fields for an expanded choice field. + * + * @param FormBuilderInterface $builder The form builder. + * @param array $choiceViews The choice view objects. + * @param array $options The build options. + */ + private function addSubForms(FormBuilderInterface $builder, array $choiceViews, array $options) + { + foreach ($choiceViews as $i => $choiceView) { + if (is_array($choiceView)) { + // Flatten groups + $this->addSubForms($builder, $choiceView, $options); + } else { + $choiceOpts = array( + 'value' => $choiceView->value, + 'label' => $choiceView->label, + 'translation_domain' => $options['translation_domain'], + ); + + if ($options['multiple']) { + $choiceType = 'checkbox'; + // The user can check 0 or more checkboxes. If required + // is true, he is required to check all of them. + $choiceOpts['required'] = false; + } else { + $choiceType = 'radio'; + } + + $builder->add($i, $choiceType, $choiceOpts); + } + } + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/CollectionType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/CollectionType.php new file mode 100644 index 00000000..0cb3af1b --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/CollectionType.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\Extension\Core\EventListener\ResizeFormListener; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class CollectionType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + if ($options['allow_add'] && $options['prototype']) { + $prototype = $builder->create($options['prototype_name'], $options['type'], array_replace(array( + 'label' => $options['prototype_name'].'label__', + ), $options['options'])); + $builder->setAttribute('prototype', $prototype->getForm()); + } + + $resizeListener = new ResizeFormListener( + $options['type'], + $options['options'], + $options['allow_add'], + $options['allow_delete'] + ); + + $builder->addEventSubscriber($resizeListener); + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $view->vars = array_replace($view->vars, array( + 'allow_add' => $options['allow_add'], + 'allow_delete' => $options['allow_delete'], + )); + + if ($form->getConfig()->hasAttribute('prototype')) { + $view->vars['prototype'] = $form->getConfig()->getAttribute('prototype')->createView($view); + } + } + + /** + * {@inheritdoc} + */ + public function finishView(FormView $view, FormInterface $form, array $options) + { + if ($form->getConfig()->hasAttribute('prototype') && $view->vars['prototype']->vars['multipart']) { + $view->vars['multipart'] = true; + } + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $optionsNormalizer = function (Options $options, $value) { + $value['block_name'] = 'entry'; + + return $value; + }; + + $resolver->setDefaults(array( + 'allow_add' => false, + 'allow_delete' => false, + 'prototype' => true, + 'prototype_name' => '__name__', + 'type' => 'text', + 'options' => array(), + )); + + $resolver->setNormalizers(array( + 'options' => $optionsNormalizer, + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'collection'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/CountryType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/CountryType.php new file mode 100644 index 00000000..3482ba66 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/CountryType.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Intl\Intl; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class CountryType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'choices' => Intl::getRegionBundle()->getCountryNames(), + )); + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'choice'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'country'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php new file mode 100644 index 00000000..3a925e3a --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Intl\Intl; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class CurrencyType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'choices' => Intl::getCurrencyBundle()->getCurrencyNames(), + )); + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'choice'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'currency'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php new file mode 100644 index 00000000..a612b6fc --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php @@ -0,0 +1,281 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\ReversedTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\DataTransformerChain; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToRfc3339Transformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\ArrayToPartsTransformer; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class DateTimeType extends AbstractType +{ + const DEFAULT_DATE_FORMAT = \IntlDateFormatter::MEDIUM; + + const DEFAULT_TIME_FORMAT = \IntlDateFormatter::MEDIUM; + + /** + * This is not quite the HTML5 format yet, because ICU lacks the + * capability of parsing and generating RFC 3339 dates, which + * are like the below pattern but with a timezone suffix. The + * timezone suffix is + * + * * "Z" for UTC + * * "(-|+)HH:mm" for other timezones (note the colon!) + * + * For more information see: + * + * http://userguide.icu-project.org/formatparse/datetime#TOC-Date-Time-Format-Syntax + * http://www.w3.org/TR/html-markup/input.datetime.html + * http://tools.ietf.org/html/rfc3339 + * + * An ICU ticket was created: + * http://icu-project.org/trac/ticket/9421 + * + * It was supposedly fixed, but is not available in all PHP installations + * yet. To temporarily circumvent this issue, DateTimeToRfc3339Transformer + * is used when the format matches this constant. + */ + const HTML5_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"; + + private static $acceptedFormats = array( + \IntlDateFormatter::FULL, + \IntlDateFormatter::LONG, + \IntlDateFormatter::MEDIUM, + \IntlDateFormatter::SHORT, + ); + + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $parts = array('year', 'month', 'day', 'hour'); + $dateParts = array('year', 'month', 'day'); + $timeParts = array('hour'); + + if ($options['with_minutes']) { + $parts[] = 'minute'; + $timeParts[] = 'minute'; + } + + if ($options['with_seconds']) { + $parts[] = 'second'; + $timeParts[] = 'second'; + } + + $dateFormat = is_int($options['date_format']) ? $options['date_format'] : self::DEFAULT_DATE_FORMAT; + $timeFormat = self::DEFAULT_TIME_FORMAT; + $calendar = \IntlDateFormatter::GREGORIAN; + $pattern = is_string($options['format']) ? $options['format'] : null; + + if (!in_array($dateFormat, self::$acceptedFormats, true)) { + throw new InvalidOptionsException('The "date_format" option must be one of the IntlDateFormatter constants (FULL, LONG, MEDIUM, SHORT) or a string representing a custom format.'); + } + + if ('single_text' === $options['widget']) { + if (self::HTML5_FORMAT === $pattern) { + $builder->addViewTransformer(new DateTimeToRfc3339Transformer( + $options['model_timezone'], + $options['view_timezone'] + )); + } else { + $builder->addViewTransformer(new DateTimeToLocalizedStringTransformer( + $options['model_timezone'], + $options['view_timezone'], + $dateFormat, + $timeFormat, + $calendar, + $pattern + )); + } + } else { + // Only pass a subset of the options to children + $dateOptions = array_intersect_key($options, array_flip(array( + 'years', + 'months', + 'days', + 'empty_value', + 'required', + 'translation_domain', + ))); + + $timeOptions = array_intersect_key($options, array_flip(array( + 'hours', + 'minutes', + 'seconds', + 'with_minutes', + 'with_seconds', + 'empty_value', + 'required', + 'translation_domain', + ))); + + if (null !== $options['date_widget']) { + $dateOptions['widget'] = $options['date_widget']; + } + + if (null !== $options['time_widget']) { + $timeOptions['widget'] = $options['time_widget']; + } + + if (null !== $options['date_format']) { + $dateOptions['format'] = $options['date_format']; + } + + $dateOptions['input'] = $timeOptions['input'] = 'array'; + $dateOptions['error_bubbling'] = $timeOptions['error_bubbling'] = true; + + $builder + ->addViewTransformer(new DataTransformerChain(array( + new DateTimeToArrayTransformer($options['model_timezone'], $options['view_timezone'], $parts), + new ArrayToPartsTransformer(array( + 'date' => $dateParts, + 'time' => $timeParts, + )), + ))) + ->add('date', 'date', $dateOptions) + ->add('time', 'time', $timeOptions) + ; + } + + if ('string' === $options['input']) { + $builder->addModelTransformer(new ReversedTransformer( + new DateTimeToStringTransformer($options['model_timezone'], $options['model_timezone']) + )); + } elseif ('timestamp' === $options['input']) { + $builder->addModelTransformer(new ReversedTransformer( + new DateTimeToTimestampTransformer($options['model_timezone'], $options['model_timezone']) + )); + } elseif ('array' === $options['input']) { + $builder->addModelTransformer(new ReversedTransformer( + new DateTimeToArrayTransformer($options['model_timezone'], $options['model_timezone'], $parts) + )); + } + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $view->vars['widget'] = $options['widget']; + + // Change the input to a HTML5 date input if + // * the widget is set to "single_text" + // * the format matches the one expected by HTML5 + if ('single_text' === $options['widget'] && self::HTML5_FORMAT === $options['format']) { + $view->vars['type'] = 'datetime'; + } + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $compound = function (Options $options) { + return $options['widget'] !== 'single_text'; + }; + + // Defaults to the value of "widget" + $dateWidget = function (Options $options) { + return $options['widget']; + }; + + // Defaults to the value of "widget" + $timeWidget = function (Options $options) { + return $options['widget']; + }; + + $resolver->setDefaults(array( + 'input' => 'datetime', + 'model_timezone' => null, + 'view_timezone' => null, + 'format' => self::HTML5_FORMAT, + 'date_format' => null, + 'widget' => null, + 'date_widget' => $dateWidget, + 'time_widget' => $timeWidget, + 'with_minutes' => true, + 'with_seconds' => false, + // Don't modify \DateTime classes by reference, we treat + // them like immutable value objects + 'by_reference' => false, + 'error_bubbling' => false, + // If initialized with a \DateTime object, FormType initializes + // this option to "\DateTime". Since the internal, normalized + // representation is not \DateTime, but an array, we need to unset + // this option. + 'data_class' => null, + 'compound' => $compound, + )); + + // Don't add some defaults in order to preserve the defaults + // set in DateType and TimeType + $resolver->setOptional(array( + 'empty_value', + 'years', + 'months', + 'days', + 'hours', + 'minutes', + 'seconds', + )); + + $resolver->setAllowedValues(array( + 'input' => array( + 'datetime', + 'string', + 'timestamp', + 'array', + ), + 'date_widget' => array( + null, // inherit default from DateType + 'single_text', + 'text', + 'choice', + ), + 'time_widget' => array( + null, // inherit default from TimeType + 'single_text', + 'text', + 'choice', + ), + // This option will overwrite "date_widget" and "time_widget" options + 'widget' => array( + null, // default, don't overwrite options + 'single_text', + 'text', + 'choice', + ), + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'datetime'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/DateType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/DateType.php new file mode 100644 index 00000000..93d3502e --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/DateType.php @@ -0,0 +1,309 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer; +use Symfony\Component\Form\ReversedTransformer; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; +use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; + +class DateType extends AbstractType +{ + const DEFAULT_FORMAT = \IntlDateFormatter::MEDIUM; + + const HTML5_FORMAT = 'yyyy-MM-dd'; + + private static $acceptedFormats = array( + \IntlDateFormatter::FULL, + \IntlDateFormatter::LONG, + \IntlDateFormatter::MEDIUM, + \IntlDateFormatter::SHORT, + ); + + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $dateFormat = is_int($options['format']) ? $options['format'] : self::DEFAULT_FORMAT; + $timeFormat = \IntlDateFormatter::NONE; + $calendar = \IntlDateFormatter::GREGORIAN; + $pattern = is_string($options['format']) ? $options['format'] : null; + + if (!in_array($dateFormat, self::$acceptedFormats, true)) { + throw new InvalidOptionsException('The "format" option must be one of the IntlDateFormatter constants (FULL, LONG, MEDIUM, SHORT) or a string representing a custom format.'); + } + + if (null !== $pattern && (false === strpos($pattern, 'y') || false === strpos($pattern, 'M') || false === strpos($pattern, 'd'))) { + throw new InvalidOptionsException(sprintf('The "format" option should contain the letters "y", "M" and "d". Its current value is "%s".', $pattern)); + } + + if ('single_text' === $options['widget']) { + $builder->addViewTransformer(new DateTimeToLocalizedStringTransformer( + $options['model_timezone'], + $options['view_timezone'], + $dateFormat, + $timeFormat, + $calendar, + $pattern + )); + } else { + $yearOptions = $monthOptions = $dayOptions = array( + 'error_bubbling' => true, + ); + + $formatter = new \IntlDateFormatter( + \Locale::getDefault(), + $dateFormat, + $timeFormat, + 'UTC', + $calendar, + $pattern + ); + $formatter->setLenient(false); + + if ('choice' === $options['widget']) { + // Only pass a subset of the options to children + $yearOptions['choices'] = $this->formatTimestamps($formatter, '/y+/', $this->listYears($options['years'])); + $yearOptions['empty_value'] = $options['empty_value']['year']; + $monthOptions['choices'] = $this->formatTimestamps($formatter, '/[M|L]+/', $this->listMonths($options['months'])); + $monthOptions['empty_value'] = $options['empty_value']['month']; + $dayOptions['choices'] = $this->formatTimestamps($formatter, '/d+/', $this->listDays($options['days'])); + $dayOptions['empty_value'] = $options['empty_value']['day']; + } + + // Append generic carry-along options + foreach (array('required', 'translation_domain') as $passOpt) { + $yearOptions[$passOpt] = $monthOptions[$passOpt] = $dayOptions[$passOpt] = $options[$passOpt]; + } + + $builder + ->add('year', $options['widget'], $yearOptions) + ->add('month', $options['widget'], $monthOptions) + ->add('day', $options['widget'], $dayOptions) + ->addViewTransformer(new DateTimeToArrayTransformer( + $options['model_timezone'], $options['view_timezone'], array('year', 'month', 'day') + )) + ->setAttribute('formatter', $formatter) + ; + } + + if ('string' === $options['input']) { + $builder->addModelTransformer(new ReversedTransformer( + new DateTimeToStringTransformer($options['model_timezone'], $options['model_timezone'], 'Y-m-d') + )); + } elseif ('timestamp' === $options['input']) { + $builder->addModelTransformer(new ReversedTransformer( + new DateTimeToTimestampTransformer($options['model_timezone'], $options['model_timezone']) + )); + } elseif ('array' === $options['input']) { + $builder->addModelTransformer(new ReversedTransformer( + new DateTimeToArrayTransformer($options['model_timezone'], $options['model_timezone'], array('year', 'month', 'day')) + )); + } + } + + /** + * {@inheritdoc} + */ + public function finishView(FormView $view, FormInterface $form, array $options) + { + $view->vars['widget'] = $options['widget']; + + // Change the input to a HTML5 date input if + // * the widget is set to "single_text" + // * the format matches the one expected by HTML5 + if ('single_text' === $options['widget'] && self::HTML5_FORMAT === $options['format']) { + $view->vars['type'] = 'date'; + } + + if ($form->getConfig()->hasAttribute('formatter')) { + $pattern = $form->getConfig()->getAttribute('formatter')->getPattern(); + + // remove special characters unless the format was explicitly specified + if (!is_string($options['format'])) { + $pattern = preg_replace('/[^yMd]+/', '', $pattern); + } + + // set right order with respect to locale (e.g.: de_DE=dd.MM.yy; en_US=M/d/yy) + // lookup various formats at http://userguide.icu-project.org/formatparse/datetime + if (preg_match('/^([yMd]+)[^yMd]*([yMd]+)[^yMd]*([yMd]+)$/', $pattern)) { + $pattern = preg_replace(array('/y+/', '/M+/', '/d+/'), array('{{ year }}', '{{ month }}', '{{ day }}'), $pattern); + } else { + // default fallback + $pattern = '{{ year }}{{ month }}{{ day }}'; + } + + $view->vars['date_pattern'] = $pattern; + } + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $compound = function (Options $options) { + return $options['widget'] !== 'single_text'; + }; + + $emptyValue = $emptyValueDefault = function (Options $options) { + return $options['required'] ? null : ''; + }; + + $emptyValueNormalizer = function (Options $options, $emptyValue) use ($emptyValueDefault) { + if (is_array($emptyValue)) { + $default = $emptyValueDefault($options); + + return array_merge( + array('year' => $default, 'month' => $default, 'day' => $default), + $emptyValue + ); + } + + return array( + 'year' => $emptyValue, + 'month' => $emptyValue, + 'day' => $emptyValue + ); + }; + + $format = function (Options $options) { + return $options['widget'] === 'single_text' ? DateType::HTML5_FORMAT : DateType::DEFAULT_FORMAT; + }; + + $resolver->setDefaults(array( + 'years' => range(date('Y') - 5, date('Y') + 5), + 'months' => range(1, 12), + 'days' => range(1, 31), + 'widget' => 'choice', + 'input' => 'datetime', + 'format' => $format, + 'model_timezone' => null, + 'view_timezone' => null, + 'empty_value' => $emptyValue, + // Don't modify \DateTime classes by reference, we treat + // them like immutable value objects + 'by_reference' => false, + 'error_bubbling' => false, + // If initialized with a \DateTime object, FormType initializes + // this option to "\DateTime". Since the internal, normalized + // representation is not \DateTime, but an array, we need to unset + // this option. + 'data_class' => null, + 'compound' => $compound, + )); + + $resolver->setNormalizers(array( + 'empty_value' => $emptyValueNormalizer, + )); + + $resolver->setAllowedValues(array( + 'input' => array( + 'datetime', + 'string', + 'timestamp', + 'array', + ), + 'widget' => array( + 'single_text', + 'text', + 'choice', + ), + )); + + $resolver->setAllowedTypes(array( + 'format' => array('int', 'string'), + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'date'; + } + + private function formatTimestamps(\IntlDateFormatter $formatter, $regex, array $timestamps) + { + $pattern = $formatter->getPattern(); + $timezone = $formatter->getTimezoneId(); + + if (version_compare(\PHP_VERSION, '5.5.0-dev', '>=')) { + $formatter->setTimeZone(\DateTimeZone::UTC); + } else { + $formatter->setTimeZoneId(\DateTimeZone::UTC); + } + + if (preg_match($regex, $pattern, $matches)) { + $formatter->setPattern($matches[0]); + + foreach ($timestamps as $key => $timestamp) { + $timestamps[$key] = $formatter->format($timestamp); + } + + // I'd like to clone the formatter above, but then we get a + // segmentation fault, so let's restore the old state instead + $formatter->setPattern($pattern); + } + + if (version_compare(\PHP_VERSION, '5.5.0-dev', '>=')) { + $formatter->setTimeZone($timezone); + } else { + $formatter->setTimeZoneId($timezone); + } + + return $timestamps; + } + + private function listYears(array $years) + { + $result = array(); + + foreach ($years as $year) { + $result[$year] = gmmktime(0, 0, 0, 6, 15, $year); + } + + return $result; + } + + private function listMonths(array $months) + { + $result = array(); + + foreach ($months as $month) { + $result[$month] = gmmktime(0, 0, 0, $month, 15); + } + + return $result; + } + + private function listDays(array $days) + { + $result = array(); + + foreach ($days as $day) { + $result[$day] = gmmktime(0, 0, 0, 5, $day); + } + + return $result; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/EmailType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/EmailType.php new file mode 100644 index 00000000..26652ef6 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/EmailType.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; + +class EmailType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'text'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'email'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/FileType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/FileType.php new file mode 100644 index 00000000..2c09da6f --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/FileType.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class FileType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $view->vars = array_replace($view->vars, array( + 'type' => 'file', + 'value' => '', + )); + } + + /** + * {@inheritdoc} + */ + public function finishView(FormView $view, FormInterface $form, array $options) + { + $view + ->vars['multipart'] = true + ; + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'compound' => false, + 'data_class' => 'Symfony\Component\HttpFoundation\File\File', + 'empty_data' => null, + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'file'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/FormType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/FormType.php new file mode 100644 index 00000000..0c39d3eb --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/FormType.php @@ -0,0 +1,214 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\Extension\Core\EventListener\TrimListener; +use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper; +use Symfony\Component\Form\Exception\LogicException; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; +use Symfony\Component\PropertyAccess\PropertyAccess; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; + +class FormType extends BaseType +{ + /** + * @var PropertyAccessorInterface + */ + private $propertyAccessor; + + public function __construct(PropertyAccessorInterface $propertyAccessor = null) + { + $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::getPropertyAccessor(); + } + + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + parent::buildForm($builder, $options); + + $builder + ->setRequired($options['required']) + ->setErrorBubbling($options['error_bubbling']) + ->setEmptyData($options['empty_data']) + ->setPropertyPath($options['property_path']) + ->setMapped($options['mapped']) + ->setByReference($options['by_reference']) + ->setInheritData($options['inherit_data']) + ->setCompound($options['compound']) + ->setData(isset($options['data']) ? $options['data'] : null) + ->setDataLocked(isset($options['data'])) + ->setDataMapper($options['compound'] ? new PropertyPathMapper($this->propertyAccessor) : null) + ->setMethod($options['method']) + ->setAction($options['action']) + ->setAutoInitialize($options['auto_initialize']) + ; + + if ($options['trim']) { + $builder->addEventSubscriber(new TrimListener()); + } + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + parent::buildView($view, $form, $options); + + $name = $form->getName(); + $readOnly = $options['read_only']; + + if ($view->parent) { + if ('' === $name) { + throw new LogicException('Form node with empty name can be used only as root form node.'); + } + + // Complex fields are read-only if they themselves or their parents are. + if (!$readOnly) { + $readOnly = $view->parent->vars['read_only']; + } + } + + $view->vars = array_replace($view->vars, array( + 'read_only' => $readOnly, + 'errors' => $form->getErrors(), + 'valid' => $form->isSubmitted() ? $form->isValid() : true, + 'value' => $form->getViewData(), + 'data' => $form->getNormData(), + 'required' => $form->isRequired(), + 'max_length' => $options['max_length'], + 'pattern' => $options['pattern'], + 'size' => null, + 'label_attr' => $options['label_attr'], + 'compound' => $form->getConfig()->getCompound(), + 'method' => $form->getConfig()->getMethod(), + 'action' => $form->getConfig()->getAction(), + )); + } + + /** + * {@inheritdoc} + */ + public function finishView(FormView $view, FormInterface $form, array $options) + { + $multipart = false; + + foreach ($view->children as $child) { + if ($child->vars['multipart']) { + $multipart = true; + break; + } + } + + $view->vars['multipart'] = $multipart; + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + parent::setDefaultOptions($resolver); + + // Derive "data_class" option from passed "data" object + $dataClass = function (Options $options) { + return isset($options['data']) && is_object($options['data']) ? get_class($options['data']) : null; + }; + + // Derive "empty_data" closure from "data_class" option + $emptyData = function (Options $options) { + $class = $options['data_class']; + + if (null !== $class) { + return function (FormInterface $form) use ($class) { + return $form->isEmpty() && !$form->isRequired() ? null : new $class(); + }; + } + + return function (FormInterface $form) { + return $form->getConfig()->getCompound() ? array() : ''; + }; + }; + + // For any form that is not represented by a single HTML control, + // errors should bubble up by default + $errorBubbling = function (Options $options) { + return $options['compound']; + }; + + // BC with old "virtual" option + $inheritData = function (Options $options) { + if (null !== $options['virtual']) { + // Uncomment this as soon as the deprecation note should be shown + // trigger_error('The form option "virtual" is deprecated since version 2.3 and will be removed in 3.0. Use "inherit_data" instead.', E_USER_DEPRECATED); + return $options['virtual']; + } + + return false; + }; + + // If data is given, the form is locked to that data + // (independent of its value) + $resolver->setOptional(array( + 'data', + )); + + $resolver->setDefaults(array( + 'data_class' => $dataClass, + 'empty_data' => $emptyData, + 'trim' => true, + 'required' => true, + 'read_only' => false, + 'max_length' => null, + 'pattern' => null, + 'property_path' => null, + 'mapped' => true, + 'by_reference' => true, + 'error_bubbling' => $errorBubbling, + 'label_attr' => array(), + 'virtual' => null, + 'inherit_data' => $inheritData, + 'compound' => true, + 'method' => 'POST', + // According to RFC 2396 (http://www.ietf.org/rfc/rfc2396.txt) + // section 4.2., empty URIs are considered same-document references + 'action' => '', + 'auto_initialize' => true, + )); + + $resolver->setAllowedTypes(array( + 'label_attr' => 'array', + )); + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return null; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'form'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/HiddenType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/HiddenType.php new file mode 100644 index 00000000..bd4fa898 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/HiddenType.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class HiddenType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + // hidden fields cannot have a required attribute + 'required' => false, + // Pass errors to the parent + 'error_bubbling' => true, + 'compound' => false, + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'hidden'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/IntegerType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/IntegerType.php new file mode 100644 index 00000000..b224cac5 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/IntegerType.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\Extension\Core\DataTransformer\IntegerToLocalizedStringTransformer; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class IntegerType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->addViewTransformer( + new IntegerToLocalizedStringTransformer( + $options['precision'], + $options['grouping'], + $options['rounding_mode'] + )); + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + // default precision is locale specific (usually around 3) + 'precision' => null, + 'grouping' => false, + // Integer cast rounds towards 0, so do the same when displaying fractions + 'rounding_mode' => \NumberFormatter::ROUND_DOWN, + 'compound' => false, + )); + + $resolver->setAllowedValues(array( + 'rounding_mode' => array( + \NumberFormatter::ROUND_FLOOR, + \NumberFormatter::ROUND_DOWN, + \NumberFormatter::ROUND_HALFDOWN, + \NumberFormatter::ROUND_HALFEVEN, + \NumberFormatter::ROUND_HALFUP, + \NumberFormatter::ROUND_UP, + \NumberFormatter::ROUND_CEILING, + ), + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'integer'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/LanguageType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/LanguageType.php new file mode 100644 index 00000000..37b2bf33 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/LanguageType.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Intl\Intl; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class LanguageType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'choices' => Intl::getLanguageBundle()->getLanguageNames(), + )); + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'choice'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'language'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/LocaleType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/LocaleType.php new file mode 100644 index 00000000..c68c561a --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/LocaleType.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Intl\Intl; +use Symfony\Component\Locale\Locale; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class LocaleType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'choices' => Intl::getLocaleBundle()->getLocaleNames(), + )); + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'choice'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'locale'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/MoneyType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/MoneyType.php new file mode 100644 index 00000000..9e36f9ce --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/MoneyType.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\Extension\Core\DataTransformer\MoneyToLocalizedStringTransformer; +use Symfony\Component\Form\FormView; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class MoneyType extends AbstractType +{ + protected static $patterns = array(); + + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder + ->addViewTransformer(new MoneyToLocalizedStringTransformer( + $options['precision'], + $options['grouping'], + null, + $options['divisor'] + )) + ; + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $view->vars['money_pattern'] = self::getPattern($options['currency']); + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'precision' => 2, + 'grouping' => false, + 'divisor' => 1, + 'currency' => 'EUR', + 'compound' => false, + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'money'; + } + + /** + * Returns the pattern for this locale + * + * The pattern contains the placeholder "{{ widget }}" where the HTML tag should + * be inserted + */ + protected static function getPattern($currency) + { + if (!$currency) { + return '{{ widget }}'; + } + + $locale = \Locale::getDefault(); + + if (!isset(self::$patterns[$locale])) { + self::$patterns[$locale] = array(); + } + + if (!isset(self::$patterns[$locale][$currency])) { + $format = new \NumberFormatter($locale, \NumberFormatter::CURRENCY); + $pattern = $format->formatCurrency('123', $currency); + + // the spacings between currency symbol and number are ignored, because + // a single space leads to better readability in combination with input + // fields + + // the regex also considers non-break spaces (0xC2 or 0xA0 in UTF-8) + + preg_match('/^([^\s\xc2\xa0]*)[\s\xc2\xa0]*123(?:[,.]0+)?[\s\xc2\xa0]*([^\s\xc2\xa0]*)$/u', $pattern, $matches); + + if (!empty($matches[1])) { + self::$patterns[$locale][$currency] = $matches[1].' {{ widget }}'; + } elseif (!empty($matches[2])) { + self::$patterns[$locale][$currency] = '{{ widget }} '.$matches[2]; + } else { + self::$patterns[$locale][$currency] = '{{ widget }}'; + } + } + + return self::$patterns[$locale][$currency]; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/NumberType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/NumberType.php new file mode 100644 index 00000000..beb3c89a --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/NumberType.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\Extension\Core\DataTransformer\NumberToLocalizedStringTransformer; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class NumberType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->addViewTransformer(new NumberToLocalizedStringTransformer( + $options['precision'], + $options['grouping'], + $options['rounding_mode'] + )); + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + // default precision is locale specific (usually around 3) + 'precision' => null, + 'grouping' => false, + 'rounding_mode' => \NumberFormatter::ROUND_HALFUP, + 'compound' => false, + )); + + $resolver->setAllowedValues(array( + 'rounding_mode' => array( + \NumberFormatter::ROUND_FLOOR, + \NumberFormatter::ROUND_DOWN, + \NumberFormatter::ROUND_HALFDOWN, + \NumberFormatter::ROUND_HALFEVEN, + \NumberFormatter::ROUND_HALFUP, + \NumberFormatter::ROUND_UP, + \NumberFormatter::ROUND_CEILING, + ), + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'number'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/PasswordType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/PasswordType.php new file mode 100644 index 00000000..5a5b1635 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/PasswordType.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class PasswordType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + if ($options['always_empty'] || !$form->isSubmitted()) { + $view->vars['value'] = ''; + } + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'always_empty' => true, + 'trim' => false, + )); + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'text'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'password'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/PercentType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/PercentType.php new file mode 100644 index 00000000..b1df9436 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/PercentType.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\Extension\Core\DataTransformer\PercentToLocalizedStringTransformer; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class PercentType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->addViewTransformer(new PercentToLocalizedStringTransformer($options['precision'], $options['type'])); + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'precision' => 0, + 'type' => 'fractional', + 'compound' => false, + )); + + $resolver->setAllowedValues(array( + 'type' => array( + 'fractional', + 'integer', + ), + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'percent'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/RadioType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/RadioType.php new file mode 100644 index 00000000..dfa7c7d5 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/RadioType.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; + +class RadioType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'checkbox'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'radio'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/RepeatedType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/RepeatedType.php new file mode 100644 index 00000000..9a3cd146 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/RepeatedType.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\Extension\Core\DataTransformer\ValueToDuplicatesTransformer; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class RepeatedType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + // Overwrite required option for child fields + $options['first_options']['required'] = $options['required']; + $options['second_options']['required'] = $options['required']; + + if (!isset($options['options']['error_bubbling'])) { + $options['options']['error_bubbling'] = $options['error_bubbling']; + } + + $builder + ->addViewTransformer(new ValueToDuplicatesTransformer(array( + $options['first_name'], + $options['second_name'], + ))) + ->add($options['first_name'], $options['type'], array_merge($options['options'], $options['first_options'])) + ->add($options['second_name'], $options['type'], array_merge($options['options'], $options['second_options'])) + ; + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'type' => 'text', + 'options' => array(), + 'first_options' => array(), + 'second_options' => array(), + 'first_name' => 'first', + 'second_name' => 'second', + 'error_bubbling' => false, + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'repeated'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/ResetType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/ResetType.php new file mode 100644 index 00000000..cf55f7c5 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/ResetType.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\ButtonTypeInterface; + +/** + * A reset button. + * + * @author Bernhard Schussek + */ +class ResetType extends AbstractType implements ButtonTypeInterface +{ + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'button'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'reset'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/SearchType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/SearchType.php new file mode 100644 index 00000000..bf82972d --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/SearchType.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; + +class SearchType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'text'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'search'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/SubmitType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/SubmitType.php new file mode 100644 index 00000000..6d160b96 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/SubmitType.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\SubmitButtonTypeInterface; + +/** + * A submit button. + * + * @author Bernhard Schussek + */ +class SubmitType extends AbstractType implements SubmitButtonTypeInterface +{ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $view->vars['clicked'] = $form->isClicked(); + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'button'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'submit'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/TextType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/TextType.php new file mode 100644 index 00000000..11503261 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/TextType.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class TextType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'compound' => false, + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'text'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/TextareaType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/TextareaType.php new file mode 100644 index 00000000..0e749b15 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/TextareaType.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\FormInterface; + +class TextareaType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $view->vars['pattern'] = null; + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'text'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'textarea'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/TimeType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/TimeType.php new file mode 100644 index 00000000..d7a2a9ef --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/TimeType.php @@ -0,0 +1,225 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\ReversedTransformer; +use Symfony\Component\Form\Exception\InvalidConfigurationException; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer; +use Symfony\Component\Form\FormView; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class TimeType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $parts = array('hour'); + $format = 'H'; + + if ($options['with_seconds'] && !$options['with_minutes']) { + throw new InvalidConfigurationException('You can not disable minutes if you have enabled seconds.'); + } + + if ($options['with_minutes']) { + $format .= ':i'; + $parts[] = 'minute'; + } + + if ($options['with_seconds']) { + $format .= ':s'; + $parts[] = 'second'; + } + + if ('single_text' === $options['widget']) { + $builder->addViewTransformer(new DateTimeToStringTransformer($options['model_timezone'], $options['view_timezone'], $format)); + } else { + $hourOptions = $minuteOptions = $secondOptions = array( + 'error_bubbling' => true, + ); + + if ('choice' === $options['widget']) { + $hours = $minutes = array(); + + foreach ($options['hours'] as $hour) { + $hours[$hour] = str_pad($hour, 2, '0', STR_PAD_LEFT); + } + + // Only pass a subset of the options to children + $hourOptions['choices'] = $hours; + $hourOptions['empty_value'] = $options['empty_value']['hour']; + + if ($options['with_minutes']) { + foreach ($options['minutes'] as $minute) { + $minutes[$minute] = str_pad($minute, 2, '0', STR_PAD_LEFT); + } + + $minuteOptions['choices'] = $minutes; + $minuteOptions['empty_value'] = $options['empty_value']['minute']; + } + + if ($options['with_seconds']) { + $seconds = array(); + + foreach ($options['seconds'] as $second) { + $seconds[$second] = str_pad($second, 2, '0', STR_PAD_LEFT); + } + + $secondOptions['choices'] = $seconds; + $secondOptions['empty_value'] = $options['empty_value']['second']; + } + + // Append generic carry-along options + foreach (array('required', 'translation_domain') as $passOpt) { + $hourOptions[$passOpt] = $options[$passOpt]; + + if ($options['with_minutes']) { + $minuteOptions[$passOpt] = $options[$passOpt]; + } + + if ($options['with_seconds']) { + $secondOptions[$passOpt] = $options[$passOpt]; + } + } + } + + $builder->add('hour', $options['widget'], $hourOptions); + + if ($options['with_minutes']) { + $builder->add('minute', $options['widget'], $minuteOptions); + } + + if ($options['with_seconds']) { + $builder->add('second', $options['widget'], $secondOptions); + } + + $builder->addViewTransformer(new DateTimeToArrayTransformer($options['model_timezone'], $options['view_timezone'], $parts, 'text' === $options['widget'])); + } + + if ('string' === $options['input']) { + $builder->addModelTransformer(new ReversedTransformer( + new DateTimeToStringTransformer($options['model_timezone'], $options['model_timezone'], 'H:i:s') + )); + } elseif ('timestamp' === $options['input']) { + $builder->addModelTransformer(new ReversedTransformer( + new DateTimeToTimestampTransformer($options['model_timezone'], $options['model_timezone']) + )); + } elseif ('array' === $options['input']) { + $builder->addModelTransformer(new ReversedTransformer( + new DateTimeToArrayTransformer($options['model_timezone'], $options['model_timezone'], $parts) + )); + } + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $view->vars = array_replace($view->vars, array( + 'widget' => $options['widget'], + 'with_minutes' => $options['with_minutes'], + 'with_seconds' => $options['with_seconds'], + )); + + if ('single_text' === $options['widget']) { + $view->vars['type'] = 'time'; + } + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $compound = function (Options $options) { + return $options['widget'] !== 'single_text'; + }; + + $emptyValue = $emptyValueDefault = function (Options $options) { + return $options['required'] ? null : ''; + }; + + $emptyValueNormalizer = function (Options $options, $emptyValue) use ($emptyValueDefault) { + if (is_array($emptyValue)) { + $default = $emptyValueDefault($options); + + return array_merge( + array('hour' => $default, 'minute' => $default, 'second' => $default), + $emptyValue + ); + } + + return array( + 'hour' => $emptyValue, + 'minute' => $emptyValue, + 'second' => $emptyValue + ); + }; + + $resolver->setDefaults(array( + 'hours' => range(0, 23), + 'minutes' => range(0, 59), + 'seconds' => range(0, 59), + 'widget' => 'choice', + 'input' => 'datetime', + 'with_minutes' => true, + 'with_seconds' => false, + 'model_timezone' => null, + 'view_timezone' => null, + 'empty_value' => $emptyValue, + // Don't modify \DateTime classes by reference, we treat + // them like immutable value objects + 'by_reference' => false, + 'error_bubbling' => false, + // If initialized with a \DateTime object, FormType initializes + // this option to "\DateTime". Since the internal, normalized + // representation is not \DateTime, but an array, we need to unset + // this option. + 'data_class' => null, + 'compound' => $compound, + )); + + $resolver->setNormalizers(array( + 'empty_value' => $emptyValueNormalizer, + )); + + $resolver->setAllowedValues(array( + 'input' => array( + 'datetime', + 'string', + 'timestamp', + 'array', + ), + 'widget' => array( + 'single_text', + 'text', + 'choice', + ), + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'time'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php new file mode 100644 index 00000000..cd4a2ad3 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class TimezoneType extends AbstractType +{ + /** + * Stores the available timezone choices + * @var array + */ + private static $timezones; + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'choices' => self::getTimezones(), + )); + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'choice'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'timezone'; + } + + /** + * Returns the timezone choices. + * + * The choices are generated from the ICU function + * \DateTimeZone::listIdentifiers(). They are cached during a single request, + * so multiple timezone fields on the same page don't lead to unnecessary + * overhead. + * + * @return array The timezone choices + */ + public static function getTimezones() + { + if (null === static::$timezones) { + static::$timezones = array(); + + foreach (\DateTimeZone::listIdentifiers() as $timezone) { + $parts = explode('/', $timezone); + + if (count($parts) > 2) { + $region = $parts[0]; + $name = $parts[1].' - '.$parts[2]; + } elseif (count($parts) > 1) { + $region = $parts[0]; + $name = $parts[1]; + } else { + $region = 'Other'; + $name = $parts[0]; + } + + static::$timezones[$region][$timezone] = str_replace('_', ' ', $name); + } + } + + return static::$timezones; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/UrlType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/UrlType.php new file mode 100644 index 00000000..27749b1a --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/UrlType.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\Extension\Core\EventListener\FixUrlProtocolListener; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class UrlType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->addEventSubscriber(new FixUrlProtocolListener($options['default_protocol'])); + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'default_protocol' => 'http', + )); + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'text'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'url'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/View/ChoiceView.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/View/ChoiceView.php new file mode 100644 index 00000000..97cdd214 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/View/ChoiceView.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\View; + +/** + * Represents a choice in templates. + * + * @author Bernhard Schussek + */ +class ChoiceView +{ + /** + * The original choice value. + * + * @var mixed + */ + public $data; + + /** + * The view representation of the choice. + * + * @var string + */ + public $value; + + /** + * The label displayed to humans. + * + * @var string + */ + public $label; + + /** + * Creates a new ChoiceView. + * + * @param mixed $data The original choice. + * @param string $value The view representation of the choice. + * @param string $label The label displayed to humans. + */ + public function __construct($data, $value, $label) + { + $this->data = $data; + $this->value = $value; + $this->label = $label; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php new file mode 100644 index 00000000..f9d9e40a --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Csrf; + +use Symfony\Component\Form\Extension\Csrf\Type; +use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; +use Symfony\Component\Form\AbstractExtension; +use Symfony\Component\Translation\TranslatorInterface; + +/** + * This extension protects forms by using a CSRF token. + * + * @author Bernhard Schussek + */ +class CsrfExtension extends AbstractExtension +{ + /** + * @var CsrfProviderInterface + */ + private $csrfProvider; + + /** + * @var TranslatorInterface + */ + private $translator; + + /** + * @var null|string + */ + private $translationDomain; + + /** + * Constructor. + * + * @param CsrfProviderInterface $csrfProvider The CSRF provider + * @param TranslatorInterface $translator The translator for translating error messages. + * @param null|string $translationDomain The translation domain for translating. + */ + public function __construct(CsrfProviderInterface $csrfProvider, TranslatorInterface $translator = null, $translationDomain = null) + { + $this->csrfProvider = $csrfProvider; + $this->translator = $translator; + $this->translationDomain = $translationDomain; + } + + /** + * {@inheritDoc} + */ + protected function loadTypeExtensions() + { + return array( + new Type\FormTypeCsrfExtension($this->csrfProvider, true, '_token', $this->translator, $this->translationDomain), + ); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/CsrfProvider/CsrfProviderInterface.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/CsrfProvider/CsrfProviderInterface.php new file mode 100644 index 00000000..7143b130 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/CsrfProvider/CsrfProviderInterface.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Csrf\CsrfProvider; + +/** + * Marks classes able to provide CSRF protection + * + * You can generate a CSRF token by using the method generateCsrfToken(). To + * this method you should pass a value that is unique to the page that should + * be secured against CSRF attacks. This value doesn't necessarily have to be + * secret. Implementations of this interface are responsible for adding more + * secret information. + * + * If you want to secure a form submission against CSRF attacks, you could + * supply an "intention" string. This way you make sure that the form can only + * be submitted to pages that are designed to handle the form, that is, that use + * the same intention string to validate the CSRF token with isCsrfTokenValid(). + * + * @author Bernhard Schussek + */ +interface CsrfProviderInterface +{ + /** + * Generates a CSRF token for a page of your application. + * + * @param string $intention Some value that identifies the action intention + * (i.e. "authenticate"). Doesn't have to be a secret value. + */ + public function generateCsrfToken($intention); + + /** + * Validates a CSRF token. + * + * @param string $intention The intention used when generating the CSRF token + * @param string $token The token supplied by the browser + * + * @return Boolean Whether the token supplied by the browser is correct + */ + public function isCsrfTokenValid($intention, $token); +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php new file mode 100644 index 00000000..5354886c --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Csrf\CsrfProvider; + +/** + * Default implementation of CsrfProviderInterface. + * + * This provider uses the session ID returned by session_id() as well as a + * user-defined secret value to secure the CSRF token. + * + * @author Bernhard Schussek + */ +class DefaultCsrfProvider implements CsrfProviderInterface +{ + /** + * A secret value used for generating the CSRF token + * @var string + */ + protected $secret; + + /** + * Initializes the provider with a secret value + * + * A recommended value for the secret is a generated value with at least + * 32 characters and mixed letters, digits and special characters. + * + * @param string $secret A secret value included in the CSRF token + */ + public function __construct($secret) + { + $this->secret = $secret; + } + + /** + * {@inheritDoc} + */ + public function generateCsrfToken($intention) + { + return sha1($this->secret.$intention.$this->getSessionId()); + } + + /** + * {@inheritDoc} + */ + public function isCsrfTokenValid($intention, $token) + { + return $token === $this->generateCsrfToken($intention); + } + + /** + * Returns the ID of the user session. + * + * Automatically starts the session if necessary. + * + * @return string The session ID + */ + protected function getSessionId() + { + if (version_compare(PHP_VERSION, '5.4', '>=')) { + if (PHP_SESSION_NONE === session_status()) { + session_start(); + } + } elseif (!session_id()) { + session_start(); + } + + return session_id(); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/CsrfProvider/SessionCsrfProvider.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/CsrfProvider/SessionCsrfProvider.php new file mode 100644 index 00000000..ea1fa585 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/CsrfProvider/SessionCsrfProvider.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Csrf\CsrfProvider; + +use Symfony\Component\HttpFoundation\Session\Session; + +/** + * This provider uses a Symfony2 Session object to retrieve the user's + * session ID. + * + * @see DefaultCsrfProvider + * + * @author Bernhard Schussek + */ +class SessionCsrfProvider extends DefaultCsrfProvider +{ + /** + * The user session from which the session ID is returned + * @var Session + */ + protected $session; + + /** + * Initializes the provider with a Session object and a secret value. + * + * A recommended value for the secret is a generated value with at least + * 32 characters and mixed letters, digits and special characters. + * + * @param Session $session The user session + * @param string $secret A secret value included in the CSRF token + */ + public function __construct(Session $session, $secret) + { + parent::__construct($secret); + + $this->session = $session; + } + + /** + * {@inheritdoc} + */ + protected function getSessionId() + { + $this->session->start(); + + return $this->session->getId(); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php new file mode 100644 index 00000000..547e9d75 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Csrf\EventListener; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormError; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; +use Symfony\Component\Translation\TranslatorInterface; + +/** + * @author Bernhard Schussek + */ +class CsrfValidationListener implements EventSubscriberInterface +{ + /** + * The name of the CSRF field + * @var string + */ + private $fieldName; + + /** + * The provider for generating and validating CSRF tokens + * @var CsrfProviderInterface + */ + private $csrfProvider; + + /** + * A text mentioning the intention of the CSRF token + * + * Validation of the token will only succeed if it was generated in the + * same session and with the same intention. + * + * @var string + */ + private $intention; + + /** + * The message displayed in case of an error. + * @var string + */ + private $errorMessage; + + /** + * @var TranslatorInterface + */ + private $translator; + + /** + * @var null|string + */ + private $translationDomain; + + public static function getSubscribedEvents() + { + return array( + FormEvents::PRE_SUBMIT => 'preSubmit', + ); + } + + public function __construct($fieldName, CsrfProviderInterface $csrfProvider, $intention, $errorMessage, TranslatorInterface $translator = null, $translationDomain = null) + { + $this->fieldName = $fieldName; + $this->csrfProvider = $csrfProvider; + $this->intention = $intention; + $this->errorMessage = $errorMessage; + $this->translator = $translator; + $this->translationDomain = $translationDomain; + } + + public function preSubmit(FormEvent $event) + { + $form = $event->getForm(); + $data = $event->getData(); + + if ($form->isRoot() && $form->getConfig()->getOption('compound')) { + if (!isset($data[$this->fieldName]) || !$this->csrfProvider->isCsrfTokenValid($this->intention, $data[$this->fieldName])) { + $errorMessage = $this->errorMessage; + + if (null !== $this->translator) { + $errorMessage = $this->translator->trans($errorMessage, array(), $this->translationDomain); + } + + $form->addError(new FormError($errorMessage)); + } + + if (is_array($data)) { + unset($data[$this->fieldName]); + } + } + + $event->setData($data); + } + + /** + * Alias of {@link preSubmit()}. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link preSubmit()} instead. + */ + public function preBind(FormEvent $event) + { + $this->preSubmit($event); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php new file mode 100644 index 00000000..336cf047 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Csrf\Type; + +use Symfony\Component\Form\AbstractTypeExtension; +use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; +use Symfony\Component\Form\Extension\Csrf\EventListener\CsrfValidationListener; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; +use Symfony\Component\Translation\TranslatorInterface; + +/** + * @author Bernhard Schussek + */ +class FormTypeCsrfExtension extends AbstractTypeExtension +{ + /** + * @var CsrfProviderInterface + */ + private $defaultCsrfProvider; + + /** + * @var Boolean + */ + private $defaultEnabled; + + /** + * @var string + */ + private $defaultFieldName; + + /** + * @var TranslatorInterface + */ + private $translator; + + /** + * @var null|string + */ + private $translationDomain; + + public function __construct(CsrfProviderInterface $defaultCsrfProvider, $defaultEnabled = true, $defaultFieldName = '_token', TranslatorInterface $translator = null, $translationDomain = null) + { + $this->defaultCsrfProvider = $defaultCsrfProvider; + $this->defaultEnabled = $defaultEnabled; + $this->defaultFieldName = $defaultFieldName; + $this->translator = $translator; + $this->translationDomain = $translationDomain; + } + + /** + * Adds a CSRF field to the form when the CSRF protection is enabled. + * + * @param FormBuilderInterface $builder The form builder + * @param array $options The options + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + if (!$options['csrf_protection']) { + return; + } + + $builder + ->setAttribute('csrf_factory', $builder->getFormFactory()) + ->addEventSubscriber(new CsrfValidationListener( + $options['csrf_field_name'], + $options['csrf_provider'], + $options['intention'], + $options['csrf_message'], + $this->translator, + $this->translationDomain + )) + ; + } + + /** + * Adds a CSRF field to the root form view. + * + * @param FormView $view The form view + * @param FormInterface $form The form + * @param array $options The options + */ + public function finishView(FormView $view, FormInterface $form, array $options) + { + if ($options['csrf_protection'] && !$view->parent && $options['compound']) { + $factory = $form->getConfig()->getAttribute('csrf_factory'); + $data = $options['csrf_provider']->generateCsrfToken($options['intention']); + + $csrfForm = $factory->createNamed($options['csrf_field_name'], 'hidden', $data, array( + 'mapped' => false, + )); + + $view->children[$options['csrf_field_name']] = $csrfForm->createView($view); + } + } + + /** + * {@inheritDoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'csrf_protection' => $this->defaultEnabled, + 'csrf_field_name' => $this->defaultFieldName, + 'csrf_provider' => $this->defaultCsrfProvider, + 'csrf_message' => 'The CSRF token is invalid. Please try to resubmit the form.', + 'intention' => 'unknown', + )); + } + + /** + * {@inheritDoc} + */ + public function getExtendedType() + { + return 'form'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php b/vendor/symfony/form/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php new file mode 100644 index 00000000..6637ac8c --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\DependencyInjection; + +use Symfony\Component\Form\FormExtensionInterface; +use Symfony\Component\Form\FormTypeGuesserChain; +use Symfony\Component\Form\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\ContainerInterface; + +class DependencyInjectionExtension implements FormExtensionInterface +{ + private $container; + + private $typeServiceIds; + + private $guesserServiceIds; + + private $guesser; + + private $guesserLoaded = false; + + public function __construct(ContainerInterface $container, + array $typeServiceIds, array $typeExtensionServiceIds, + array $guesserServiceIds) + { + $this->container = $container; + $this->typeServiceIds = $typeServiceIds; + $this->typeExtensionServiceIds = $typeExtensionServiceIds; + $this->guesserServiceIds = $guesserServiceIds; + } + + public function getType($name) + { + if (!isset($this->typeServiceIds[$name])) { + throw new InvalidArgumentException(sprintf('The field type "%s" is not registered with the service container.', $name)); + } + + $type = $this->container->get($this->typeServiceIds[$name]); + + if ($type->getName() !== $name) { + throw new InvalidArgumentException( + sprintf('The type name specified for the service "%s" does not match the actual name. Expected "%s", given "%s"', + $this->typeServiceIds[$name], + $name, + $type->getName() + )); + } + + return $type; + } + + public function hasType($name) + { + return isset($this->typeServiceIds[$name]); + } + + public function getTypeExtensions($name) + { + $extensions = array(); + + if (isset($this->typeExtensionServiceIds[$name])) { + foreach ($this->typeExtensionServiceIds[$name] as $serviceId) { + $extensions[] = $this->container->get($serviceId); + } + } + + return $extensions; + } + + public function hasTypeExtensions($name) + { + return isset($this->typeExtensionServiceIds[$name]); + } + + public function getTypeGuesser() + { + if (!$this->guesserLoaded) { + $this->guesserLoaded = true; + $guessers = array(); + + foreach ($this->guesserServiceIds as $serviceId) { + $guessers[] = $this->container->get($serviceId); + } + + if (count($guessers) > 0) { + $this->guesser = new FormTypeGuesserChain($guessers); + } + } + + return $this->guesser; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/HttpFoundation/EventListener/BindRequestListener.php b/vendor/symfony/form/Symfony/Component/Form/Extension/HttpFoundation/EventListener/BindRequestListener.php new file mode 100644 index 00000000..6205b98d --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/HttpFoundation/EventListener/BindRequestListener.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\HttpFoundation\EventListener; + +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Exception\LogicException; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\Request; + +/** + * @author Bernhard Schussek + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Pass the + * Request instance to {@link Form::process()} instead. + */ +class BindRequestListener implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + // High priority in order to supersede other listeners + return array(FormEvents::PRE_BIND => array('preBind', 128)); + } + + public function preBind(FormEvent $event) + { + $form = $event->getForm(); + + /* @var Request $request */ + $request = $event->getData(); + + // Only proceed if we actually deal with a Request + if (!$request instanceof Request) { + return; + } + + // Uncomment this as soon as the deprecation note should be shown + // trigger_error('Passing a Request instance to Form::submit() is deprecated since version 2.3 and will be disabled in 3.0. Call Form::process($request) instead.', E_USER_DEPRECATED); + + $name = $form->getConfig()->getName(); + $default = $form->getConfig()->getCompound() ? array() : null; + + // Store the bound data in case of a post request + switch ($request->getMethod()) { + case 'POST': + case 'PUT': + case 'DELETE': + case 'PATCH': + if ('' === $name) { + // Form bound without name + $params = $request->request->all(); + $files = $request->files->all(); + } else { + $params = $request->request->get($name, $default); + $files = $request->files->get($name, $default); + } + + if (is_array($params) && is_array($files)) { + $data = array_replace_recursive($params, $files); + } else { + $data = $params ?: $files; + } + + break; + + case 'GET': + $data = '' === $name + ? $request->query->all() + : $request->query->get($name, $default); + + break; + + default: + throw new LogicException(sprintf( + 'The request method "%s" is not supported', + $request->getMethod() + )); + } + + $event->setData($data); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationExtension.php b/vendor/symfony/form/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationExtension.php new file mode 100644 index 00000000..08bd89c9 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationExtension.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\HttpFoundation; + +use Symfony\Component\Form\AbstractExtension; + +/** + * Integrates the HttpFoundation component with the Form library. + * + * @author Bernhard Schussek + */ +class HttpFoundationExtension extends AbstractExtension +{ + protected function loadTypeExtensions() + { + return array( + new Type\FormTypeHttpFoundationExtension(), + ); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationRequestHandler.php b/vendor/symfony/form/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationRequestHandler.php new file mode 100644 index 00000000..cc485156 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationRequestHandler.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\HttpFoundation; + +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\RequestHandlerInterface; +use Symfony\Component\HttpFoundation\Request; + +/** + * A request processor using the {@link Request} class of the HttpFoundation + * component. + * + * @author Bernhard Schussek + */ +class HttpFoundationRequestHandler implements RequestHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handleRequest(FormInterface $form, $request = null) + { + if (!$request instanceof Request) { + throw new UnexpectedTypeException($request, 'Symfony\Component\HttpFoundation\Request'); + } + + $name = $form->getName(); + $method = $form->getConfig()->getMethod(); + + if ($method !== $request->getMethod()) { + return; + } + + if ('GET' === $method) { + if ('' === $name) { + $data = $request->query->all(); + } else { + // Don't submit GET requests if the form's name does not exist + // in the request + if (!$request->query->has($name)) { + return; + } + + $data = $request->query->get($name); + } + } else { + if ('' === $name) { + $params = $request->request->all(); + $files = $request->files->all(); + } else { + $default = $form->getConfig()->getCompound() ? array() : null; + $params = $request->request->get($name, $default); + $files = $request->files->get($name, $default); + } + + if (is_array($params) && is_array($files)) { + $data = array_replace_recursive($params, $files); + } else { + $data = $params ?: $files; + } + } + + // Don't auto-submit the form unless at least one field is present. + if ('' === $name && count(array_intersect_key($data, $form->all())) <= 0) { + return; + } + + $form->submit($data, 'PATCH' !== $method); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/HttpFoundation/Type/FormTypeHttpFoundationExtension.php b/vendor/symfony/form/Symfony/Component/Form/Extension/HttpFoundation/Type/FormTypeHttpFoundationExtension.php new file mode 100644 index 00000000..9b09b05c --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/HttpFoundation/Type/FormTypeHttpFoundationExtension.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\HttpFoundation\Type; + +use Symfony\Component\Form\AbstractTypeExtension; +use Symfony\Component\Form\Extension\HttpFoundation\EventListener\BindRequestListener; +use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationRequestHandler; +use Symfony\Component\Form\FormBuilderInterface; + +/** + * @author Bernhard Schussek + */ +class FormTypeHttpFoundationExtension extends AbstractTypeExtension +{ + /** + * @var BindRequestListener + */ + private $listener; + + /** + * @var HttpFoundationRequestHandler + */ + private $requestHandler; + + public function __construct() + { + $this->listener = new BindRequestListener(); + $this->requestHandler = new HttpFoundationRequestHandler(); + } + + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->addEventSubscriber($this->listener); + $builder->setRequestHandler($this->requestHandler); + } + + /** + * {@inheritdoc} + */ + public function getExtendedType() + { + return 'form'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Templating/TemplatingExtension.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Templating/TemplatingExtension.php new file mode 100644 index 00000000..573cb518 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Templating/TemplatingExtension.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Templating; + +use Symfony\Component\Form\AbstractExtension; +use Symfony\Component\Form\FormRenderer; +use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; +use Symfony\Component\Templating\PhpEngine; +use Symfony\Bundle\FrameworkBundle\Templating\Helper\FormHelper; + +/** + * Integrates the Templating component with the Form library. + * + * @author Bernhard Schussek + */ +class TemplatingExtension extends AbstractExtension +{ + public function __construct(PhpEngine $engine, CsrfProviderInterface $csrfProvider = null, array $defaultThemes = array()) + { + $engine->addHelpers(array( + new FormHelper(new FormRenderer(new TemplatingRendererEngine($engine, $defaultThemes), $csrfProvider)) + )); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Templating/TemplatingRendererEngine.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Templating/TemplatingRendererEngine.php new file mode 100644 index 00000000..c1dda60b --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Templating/TemplatingRendererEngine.php @@ -0,0 +1,125 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Templating; + +use Symfony\Component\Form\AbstractRendererEngine; +use Symfony\Component\Form\FormView; +use Symfony\Component\Templating\EngineInterface; + +/** + * @author Bernhard Schussek + */ +class TemplatingRendererEngine extends AbstractRendererEngine +{ + /** + * @var EngineInterface + */ + private $engine; + + public function __construct(EngineInterface $engine, array $defaultThemes = array()) + { + parent::__construct($defaultThemes); + + $this->engine = $engine; + } + + /** + * {@inheritdoc} + */ + public function renderBlock(FormView $view, $resource, $blockName, array $variables = array()) + { + return trim($this->engine->render($resource, $variables)); + } + + /** + * Loads the cache with the resource for a given block name. + * + * This implementation tries to load as few blocks as possible, since each block + * is represented by a template on the file system. + * + * @see getResourceForBlock() + * + * @param string $cacheKey The cache key of the form view. + * @param FormView $view The form view for finding the applying themes. + * @param string $blockName The name of the block to load. + * + * @return Boolean True if the resource could be loaded, false otherwise. + */ + protected function loadResourceForBlockName($cacheKey, FormView $view, $blockName) + { + // Recursively try to find the block in the themes assigned to $view, + // then of its parent form, then of the parent form of the parent and so on. + // When the root form is reached in this recursion, also the default + // themes are taken into account. + + // Check each theme whether it contains the searched block + if (isset($this->themes[$cacheKey])) { + for ($i = count($this->themes[$cacheKey]) - 1; $i >= 0; --$i) { + if ($this->loadResourceFromTheme($cacheKey, $blockName, $this->themes[$cacheKey][$i])) { + return true; + } + } + } + + // Check the default themes once we reach the root form without success + if (!$view->parent) { + for ($i = count($this->defaultThemes) - 1; $i >= 0; --$i) { + if ($this->loadResourceFromTheme($cacheKey, $blockName, $this->defaultThemes[$i])) { + return true; + } + } + } + + // If we did not find anything in the themes of the current view, proceed + // with the themes of the parent view + if ($view->parent) { + $parentCacheKey = $view->parent->vars[self::CACHE_KEY_VAR]; + + if (!isset($this->resources[$parentCacheKey][$blockName])) { + $this->loadResourceForBlockName($parentCacheKey, $view->parent, $blockName); + } + + // If a template exists in the parent themes, cache that template + // for the current theme as well to speed up further accesses + if ($this->resources[$parentCacheKey][$blockName]) { + $this->resources[$cacheKey][$blockName] = $this->resources[$parentCacheKey][$blockName]; + + return true; + } + } + + // Cache that we didn't find anything to speed up further accesses + $this->resources[$cacheKey][$blockName] = false; + + return false; + } + + /** + * Tries to load the resource for a block from a theme. + * + * @param string $cacheKey The cache key for storing the resource. + * @param string $blockName The name of the block to load a resource for. + * @param mixed $theme The theme to load the block from. + * + * @return Boolean True if the resource could be loaded, false otherwise. + */ + protected function loadResourceFromTheme($cacheKey, $blockName, $theme) + { + if ($this->engine->exists($templateName = $theme.':'.$blockName.'.html.php')) { + $this->resources[$cacheKey][$blockName] = $templateName; + + return true; + } + + return false; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Constraints/Form.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Constraints/Form.php new file mode 100644 index 00000000..87e33297 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Constraints/Form.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Validator\Constraints; + +use Symfony\Component\Validator\Constraint; + +/** + * @author Bernhard Schussek + */ +class Form extends Constraint +{ + /** + * Violation code marking an invalid form. + */ + const ERR_INVALID = 1; + + /** + * {@inheritdoc} + */ + public function getTargets() + { + return self::CLASS_CONSTRAINT; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php new file mode 100644 index 00000000..bad5a007 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php @@ -0,0 +1,236 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Validator\Constraints; + +use Symfony\Component\Form\ClickableInterface; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\Extension\Validator\Util\ServerParams; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidator; + +/** + * @author Bernhard Schussek + */ +class FormValidator extends ConstraintValidator +{ + /** + * @var ServerParams + */ + private $serverParams; + + /** + * Creates a validator with the given server parameters. + * + * @param ServerParams $params The server parameters. Default + * parameters are created if null. + */ + public function __construct(ServerParams $params = null) + { + $this->serverParams = $params ?: new ServerParams(); + } + + /** + * {@inheritdoc} + */ + public function validate($form, Constraint $constraint) + { + if (!$form instanceof FormInterface) { + return; + } + + /* @var FormInterface $form */ + $config = $form->getConfig(); + + if ($form->isSynchronized()) { + // Validate the form data only if transformation succeeded + $groups = self::getValidationGroups($form); + + // Validate the data against its own constraints + if (self::allowDataWalking($form)) { + foreach ($groups as $group) { + $this->context->validate($form->getData(), 'data', $group, true); + } + } + + // Validate the data against the constraints defined + // in the form + $constraints = $config->getOption('constraints'); + foreach ($constraints as $constraint) { + foreach ($groups as $group) { + if (in_array($group, $constraint->groups)) { + $this->context->validateValue($form->getData(), $constraint, 'data', $group); + + // Prevent duplicate validation + continue 2; + } + } + } + } else { + $childrenSynchronized = true; + + foreach ($form as $child) { + if (!$child->isSynchronized()) { + $childrenSynchronized = false; + break; + } + } + + // Mark the form with an error if it is not synchronized BUT all + // of its children are synchronized. If any child is not + // synchronized, an error is displayed there already and showing + // a second error in its parent form is pointless, or worse, may + // lead to duplicate errors if error bubbling is enabled on the + // child. + // See also https://github.com/symfony/symfony/issues/4359 + if ($childrenSynchronized) { + $clientDataAsString = is_scalar($form->getViewData()) + ? (string) $form->getViewData() + : gettype($form->getViewData()); + + $this->context->addViolation( + $config->getOption('invalid_message'), + array_replace(array('{{ value }}' => $clientDataAsString), $config->getOption('invalid_message_parameters')), + $form->getViewData(), + null, + Form::ERR_INVALID + ); + } + } + + // Mark the form with an error if it contains extra fields + if (count($form->getExtraData()) > 0) { + $this->context->addViolation( + $config->getOption('extra_fields_message'), + array('{{ extra_fields }}' => implode('", "', array_keys($form->getExtraData()))), + $form->getExtraData() + ); + } + + // Mark the form with an error if the uploaded size was too large + $length = $this->serverParams->getContentLength(); + + if ($form->isRoot() && null !== $length) { + $max = $this->serverParams->getPostMaxSize(); + + if (!empty($max) && $length > $max) { + $this->context->addViolation( + $config->getOption('post_max_size_message'), + array('{{ max }}' => $this->serverParams->getNormalizedIniPostMaxSize()), + $length + ); + } + } + } + + /** + * Returns whether the data of a form may be walked. + * + * @param FormInterface $form The form to test. + * + * @return Boolean Whether the graph walker may walk the data. + */ + private static function allowDataWalking(FormInterface $form) + { + $data = $form->getData(); + + // Scalar values cannot have mapped constraints + if (!is_object($data) && !is_array($data)) { + return false; + } + + // Root forms are always validated + if ($form->isRoot()) { + return true; + } + + // Non-root forms are validated if validation cascading + // is enabled in all ancestor forms + while (null !== ($form = $form->getParent())) { + if (!$form->getConfig()->getOption('cascade_validation')) { + return false; + } + } + + return true; + } + + /** + * Returns the validation groups of the given form. + * + * @param FormInterface $form The form. + * + * @return array The validation groups. + */ + private static function getValidationGroups(FormInterface $form) + { + $button = self::findClickedButton($form->getRoot()); + + if (null !== $button) { + $groups = $button->getConfig()->getOption('validation_groups'); + + if (null !== $groups) { + return self::resolveValidationGroups($groups, $form); + } + } + + do { + $groups = $form->getConfig()->getOption('validation_groups'); + + if (null !== $groups) { + return self::resolveValidationGroups($groups, $form); + } + + $form = $form->getParent(); + } while (null !== $form); + + return array(Constraint::DEFAULT_GROUP); + } + + /** + * Extracts a clicked button from a form tree, if one exists. + * + * @param FormInterface $form The root form. + * + * @return ClickableInterface|null The clicked button or null. + */ + private static function findClickedButton(FormInterface $form) + { + if ($form instanceof ClickableInterface && $form->isClicked()) { + return $form; + } + + foreach ($form as $child) { + if (null !== ($button = self::findClickedButton($child))) { + return $button; + } + } + + return null; + } + + /** + * Post-processes the validation groups option for a given form. + * + * @param array|callable $groups The validation groups. + * @param FormInterface $form The validated form. + * + * @return array The validation groups. + */ + private static function resolveValidationGroups($groups, FormInterface $form) + { + if (!is_string($groups) && is_callable($groups)) { + $groups = call_user_func($groups, $form); + } + + return (array) $groups; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php new file mode 100644 index 00000000..14147531 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Validator\EventListener; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapperInterface; +use Symfony\Component\Validator\ValidatorInterface; +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Extension\Validator\Constraints\Form; + +/** + * @author Bernhard Schussek + */ +class ValidationListener implements EventSubscriberInterface +{ + private $validator; + + private $violationMapper; + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return array(FormEvents::POST_SUBMIT => 'validateForm'); + } + + public function __construct(ValidatorInterface $validator, ViolationMapperInterface $violationMapper) + { + $this->validator = $validator; + $this->violationMapper = $violationMapper; + } + + /** + * Validates the form and its domain object. + * + * @param FormEvent $event The event object + */ + public function validateForm(FormEvent $event) + { + $form = $event->getForm(); + + if ($form->isRoot()) { + // Validate the form in group "Default" + $violations = $this->validator->validate($form); + + if (count($violations) > 0) { + foreach ($violations as $violation) { + // Allow the "invalid" constraint to be put onto + // non-synchronized forms + $allowNonSynchronized = Form::ERR_INVALID === $violation->getCode(); + + $this->violationMapper->mapViolation($violation, $form, $allowNonSynchronized); + } + } + } + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/BaseValidatorExtension.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/BaseValidatorExtension.php new file mode 100644 index 00000000..7c5e6784 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/BaseValidatorExtension.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Validator\Type; + +use Symfony\Component\Form\AbstractTypeExtension; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +/** + * Encapsulates common logic of {@link FormTypeValidatorExtension} and + * {@link SubmitTypeValidatorExtension}. + * + * @author Bernhard Schussek + */ +abstract class BaseValidatorExtension extends AbstractTypeExtension +{ + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + // Make sure that validation groups end up as null, closure or array + $validationGroupsNormalizer = function (Options $options, $groups) { + if (false === $groups) { + return array(); + } + + if (empty($groups)) { + return null; + } + + if (is_callable($groups)) { + return $groups; + } + + return (array) $groups; + }; + + $resolver->setDefaults(array( + 'validation_groups' => null, + )); + + $resolver->setNormalizers(array( + 'validation_groups' => $validationGroupsNormalizer, + )); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php new file mode 100644 index 00000000..344bddad --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Validator\Type; + +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapper; +use Symfony\Component\Form\Extension\Validator\EventListener\ValidationListener; +use Symfony\Component\Validator\ValidatorInterface; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +/** + * @author Bernhard Schussek + */ +class FormTypeValidatorExtension extends BaseValidatorExtension +{ + /** + * @var ValidatorInterface + */ + private $validator; + + /** + * @var ViolationMapper + */ + private $violationMapper; + + public function __construct(ValidatorInterface $validator) + { + $this->validator = $validator; + $this->violationMapper = new ViolationMapper(); + } + + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->addEventSubscriber(new ValidationListener($this->validator, $this->violationMapper)); + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + parent::setDefaultOptions($resolver); + + // Constraint should always be converted to an array + $constraintsNormalizer = function (Options $options, $constraints) { + return is_object($constraints) ? array($constraints) : (array) $constraints; + }; + + $resolver->setDefaults(array( + 'error_mapping' => array(), + 'constraints' => array(), + 'cascade_validation' => false, + 'invalid_message' => 'This value is not valid.', + 'invalid_message_parameters' => array(), + 'extra_fields_message' => 'This form should not contain extra fields.', + 'post_max_size_message' => 'The uploaded file was too large. Please try to upload a smaller file.', + )); + + $resolver->setNormalizers(array( + 'constraints' => $constraintsNormalizer, + )); + } + + /** + * {@inheritdoc} + */ + public function getExtendedType() + { + return 'form'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/RepeatedTypeValidatorExtension.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/RepeatedTypeValidatorExtension.php new file mode 100644 index 00000000..858ff0fa --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/RepeatedTypeValidatorExtension.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Validator\Type; + +use Symfony\Component\Form\AbstractTypeExtension; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +/** + * @author Bernhard Schussek + */ +class RepeatedTypeValidatorExtension extends AbstractTypeExtension +{ + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + // Map errors to the first field + $errorMapping = function (Options $options) { + return array('.' => $options['first_name']); + }; + + $resolver->setDefaults(array( + 'error_mapping' => $errorMapping, + )); + } + + /** + * {@inheritdoc} + */ + public function getExtendedType() + { + return 'repeated'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/SubmitTypeValidatorExtension.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/SubmitTypeValidatorExtension.php new file mode 100644 index 00000000..5aad67fb --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/SubmitTypeValidatorExtension.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Validator\Type; + +use Symfony\Component\Form\AbstractTypeExtension; + +/** + * @author Bernhard Schussek + */ +class SubmitTypeValidatorExtension extends AbstractTypeExtension +{ + /** + * {@inheritdoc} + */ + public function getExtendedType() + { + return 'submit'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Util/ServerParams.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Util/ServerParams.php new file mode 100644 index 00000000..fab6ac2a --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Util/ServerParams.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Validator\Util; + +/** + * @author Bernhard Schussek + */ +class ServerParams +{ + /** + * Returns maximum post size in bytes. + * + * @return null|integer The maximum post size in bytes + */ + public function getPostMaxSize() + { + $iniMax = $this->getNormalizedIniPostMaxSize(); + + if ('' === $iniMax) { + return null; + } + + if (preg_match('#^\+?(0X?)?(.*?)([KMG]?)$#', $iniMax, $match)) { + $shifts = array('' => 0, 'K' => 10, 'M' => 20, 'G' => 30); + $bases = array('' => 10, '0' => 8, '0X' => 16); + + return intval($match[2], $bases[$match[1]]) << $shifts[$match[3]]; + } + + return 0; + } + + /** + * Returns the normalized "post_max_size" ini setting. + * + * @return string + */ + public function getNormalizedIniPostMaxSize() + { + return strtoupper(trim(ini_get('post_max_size'))); + } + + /** + * Returns the content length of the request. + * + * @return mixed The request content length. + */ + public function getContentLength() + { + return isset($_SERVER['CONTENT_LENGTH']) + ? (int) $_SERVER['CONTENT_LENGTH'] + : null; + } + +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php new file mode 100644 index 00000000..9cff22a2 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Validator; + +use Symfony\Component\Form\Extension\Validator\Type; +use Symfony\Component\Form\Extension\Validator\Constraints\Form; +use Symfony\Component\Form\AbstractExtension; +use Symfony\Component\Validator\ValidatorInterface; +use Symfony\Component\Validator\Constraints\Valid; + +/** + * Extension supporting the Symfony2 Validator component in forms. + * + * @author Bernhard Schussek + */ +class ValidatorExtension extends AbstractExtension +{ + private $validator; + + public function __construct(ValidatorInterface $validator) + { + $this->validator = $validator; + + // Register the form constraints in the validator programmatically. + // This functionality is required when using the Form component without + // the DIC, where the XML file is loaded automatically. Thus the following + // code must be kept synchronized with validation.xml + + /** @var \Symfony\Component\Validator\Mapping\ClassMetadata $metadata */ + $metadata = $this->validator->getMetadataFactory()->getMetadataFor('Symfony\Component\Form\Form'); + $metadata->addConstraint(new Form()); + $metadata->addPropertyConstraint('children', new Valid()); + } + + public function loadTypeGuesser() + { + return new ValidatorTypeGuesser($this->validator->getMetadataFactory()); + } + + protected function loadTypeExtensions() + { + return array( + new Type\FormTypeValidatorExtension($this->validator), + new Type\RepeatedTypeValidatorExtension(), + new Type\SubmitTypeValidatorExtension(), + ); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php new file mode 100644 index 00000000..dcd9cc55 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php @@ -0,0 +1,286 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Validator; + +use Symfony\Component\Form\FormTypeGuesserInterface; +use Symfony\Component\Form\Guess\Guess; +use Symfony\Component\Form\Guess\TypeGuess; +use Symfony\Component\Form\Guess\ValueGuess; +use Symfony\Component\Validator\MetadataFactoryInterface; +use Symfony\Component\Validator\Constraint; + +class ValidatorTypeGuesser implements FormTypeGuesserInterface +{ + private $metadataFactory; + + public function __construct(MetadataFactoryInterface $metadataFactory) + { + $this->metadataFactory = $metadataFactory; + } + + /** + * {@inheritDoc} + */ + public function guessType($class, $property) + { + $guesser = $this; + + return $this->guess($class, $property, function (Constraint $constraint) use ($guesser) { + return $guesser->guessTypeForConstraint($constraint); + }); + } + + /** + * {@inheritDoc} + */ + public function guessRequired($class, $property) + { + $guesser = $this; + + return $this->guess($class, $property, function (Constraint $constraint) use ($guesser) { + return $guesser->guessRequiredForConstraint($constraint); + // If we don't find any constraint telling otherwise, we can assume + // that a field is not required (with LOW_CONFIDENCE) + }, false); + } + + /** + * {@inheritDoc} + */ + public function guessMaxLength($class, $property) + { + $guesser = $this; + + return $this->guess($class, $property, function (Constraint $constraint) use ($guesser) { + return $guesser->guessMaxLengthForConstraint($constraint); + }); + } + + /** + * {@inheritDoc} + */ + public function guessPattern($class, $property) + { + $guesser = $this; + + return $this->guess($class, $property, function (Constraint $constraint) use ($guesser) { + return $guesser->guessPatternForConstraint($constraint); + }); + } + + /** + * Guesses a field class name for a given constraint + * + * @param Constraint $constraint The constraint to guess for + * + * @return TypeGuess The guessed field class and options + */ + public function guessTypeForConstraint(Constraint $constraint) + { + switch (get_class($constraint)) { + case 'Symfony\Component\Validator\Constraints\Type': + switch ($constraint->type) { + case 'array': + return new TypeGuess('collection', array(), Guess::MEDIUM_CONFIDENCE); + case 'boolean': + case 'bool': + return new TypeGuess('checkbox', array(), Guess::MEDIUM_CONFIDENCE); + + case 'double': + case 'float': + case 'numeric': + case 'real': + return new TypeGuess('number', array(), Guess::MEDIUM_CONFIDENCE); + + case 'integer': + case 'int': + case 'long': + return new TypeGuess('integer', array(), Guess::MEDIUM_CONFIDENCE); + + case '\DateTime': + return new TypeGuess('date', array(), Guess::MEDIUM_CONFIDENCE); + + case 'string': + return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE); + } + break; + + case 'Symfony\Component\Validator\Constraints\Country': + return new TypeGuess('country', array(), Guess::HIGH_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\Date': + return new TypeGuess('date', array('input' => 'string'), Guess::HIGH_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\DateTime': + return new TypeGuess('datetime', array('input' => 'string'), Guess::HIGH_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\Email': + return new TypeGuess('email', array(), Guess::HIGH_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\File': + case 'Symfony\Component\Validator\Constraints\Image': + return new TypeGuess('file', array(), Guess::HIGH_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\Language': + return new TypeGuess('language', array(), Guess::HIGH_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\Locale': + return new TypeGuess('locale', array(), Guess::HIGH_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\Time': + return new TypeGuess('time', array('input' => 'string'), Guess::HIGH_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\Url': + return new TypeGuess('url', array(), Guess::HIGH_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\Ip': + return new TypeGuess('text', array(), Guess::MEDIUM_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\MaxLength': + case 'Symfony\Component\Validator\Constraints\MinLength': + case 'Symfony\Component\Validator\Constraints\Regex': + return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\Min': + case 'Symfony\Component\Validator\Constraints\Max': + return new TypeGuess('number', array(), Guess::LOW_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\MinCount': + case 'Symfony\Component\Validator\Constraints\MaxCount': + return new TypeGuess('collection', array(), Guess::LOW_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\True': + case 'Symfony\Component\Validator\Constraints\False': + return new TypeGuess('checkbox', array(), Guess::MEDIUM_CONFIDENCE); + } + + return null; + } + + /** + * Guesses whether a field is required based on the given constraint + * + * @param Constraint $constraint The constraint to guess for + * + * @return Guess The guess whether the field is required + */ + public function guessRequiredForConstraint(Constraint $constraint) + { + switch (get_class($constraint)) { + case 'Symfony\Component\Validator\Constraints\NotNull': + case 'Symfony\Component\Validator\Constraints\NotBlank': + case 'Symfony\Component\Validator\Constraints\True': + return new ValueGuess(true, Guess::HIGH_CONFIDENCE); + } + + return null; + } + + /** + * Guesses a field's maximum length based on the given constraint + * + * @param Constraint $constraint The constraint to guess for + * + * @return Guess The guess for the maximum length + */ + public function guessMaxLengthForConstraint(Constraint $constraint) + { + switch (get_class($constraint)) { + case 'Symfony\Component\Validator\Constraints\MaxLength': + return new ValueGuess($constraint->limit, Guess::HIGH_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\Type': + if (in_array($constraint->type, array('double', 'float', 'numeric', 'real'))) { + return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE); + } + break; + + case 'Symfony\Component\Validator\Constraints\Max': + return new ValueGuess(strlen((string) $constraint->limit), Guess::LOW_CONFIDENCE); + } + + return null; + } + + /** + * Guesses a field's pattern based on the given constraint + * + * @param Constraint $constraint The constraint to guess for + * + * @return Guess The guess for the pattern + */ + public function guessPatternForConstraint(Constraint $constraint) + { + switch (get_class($constraint)) { + case 'Symfony\Component\Validator\Constraints\MinLength': + return new ValueGuess(sprintf('.{%s,}', (string) $constraint->limit), Guess::LOW_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\Regex': + $htmlPattern = $constraint->getHtmlPattern(); + + if (null !== $htmlPattern) { + return new ValueGuess($htmlPattern, Guess::HIGH_CONFIDENCE); + } + break; + + case 'Symfony\Component\Validator\Constraints\Min': + return new ValueGuess(sprintf('.{%s,}', strlen((string) $constraint->limit)), Guess::LOW_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\Type': + if (in_array($constraint->type, array('double', 'float', 'numeric', 'real'))) { + return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE); + } + break; + } + + return null; + } + + /** + * Iterates over the constraints of a property, executes a constraints on + * them and returns the best guess + * + * @param string $class The class to read the constraints from + * @param string $property The property for which to find constraints + * @param \Closure $closure The closure that returns a guess + * for a given constraint + * @param mixed $defaultValue The default value assumed if no other value + * can be guessed. + * + * @return Guess The guessed value with the highest confidence + */ + protected function guess($class, $property, \Closure $closure, $defaultValue = null) + { + $guesses = array(); + $classMetadata = $this->metadataFactory->getMetadataFor($class); + + if ($classMetadata->hasMemberMetadatas($property)) { + $memberMetadatas = $classMetadata->getMemberMetadatas($property); + + foreach ($memberMetadatas as $memberMetadata) { + $constraints = $memberMetadata->getConstraints(); + + foreach ($constraints as $constraint) { + if ($guess = $closure($constraint)) { + $guesses[] = $guess; + } + } + } + + if (null !== $defaultValue) { + $guesses[] = new ValueGuess($defaultValue, Guess::LOW_CONFIDENCE); + } + } + + return Guess::getBestGuess($guesses); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php new file mode 100644 index 00000000..7b96efb4 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Validator\ViolationMapper; + +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\Exception\ErrorMappingException; + +/** + * @author Bernhard Schussek + */ +class MappingRule +{ + /** + * @var FormInterface + */ + private $origin; + + /** + * @var string + */ + private $propertyPath; + + /** + * @var string + */ + private $targetPath; + + public function __construct(FormInterface $origin, $propertyPath, $targetPath) + { + $this->origin = $origin; + $this->propertyPath = $propertyPath; + $this->targetPath = $targetPath; + } + + /** + * @return FormInterface + */ + public function getOrigin() + { + return $this->origin; + } + + /** + * Matches a property path against the rule path. + * + * If the rule matches, the form mapped by the rule is returned. + * Otherwise this method returns false. + * + * @param string $propertyPath The property path to match against the rule. + * + * @return null|FormInterface The mapped form or null. + */ + public function match($propertyPath) + { + if ($propertyPath === (string) $this->propertyPath) { + return $this->getTarget(); + } + + return null; + } + + /** + * Matches a property path against a prefix of the rule path. + * + * @param string $propertyPath The property path to match against the rule. + * + * @return Boolean Whether the property path is a prefix of the rule or not. + */ + public function isPrefix($propertyPath) + { + $length = strlen($propertyPath); + $prefix = substr($this->propertyPath, 0, $length); + $next = isset($this->propertyPath[$length]) ? $this->propertyPath[$length] : null; + + return $prefix === $propertyPath && ('[' === $next || '.' === $next); + } + + /** + * @return FormInterface + * + * @throws ErrorMappingException + */ + public function getTarget() + { + $childNames = explode('.', $this->targetPath); + $target = $this->origin; + + foreach ($childNames as $childName) { + if (!$target->has($childName)) { + throw new ErrorMappingException(sprintf('The child "%s" of "%s" mapped by the rule "%s" in "%s" does not exist.', $childName, $target->getName(), $this->targetPath, $this->origin->getName())); + } + $target = $target->get($childName); + } + + return $target; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/RelativePath.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/RelativePath.php new file mode 100644 index 00000000..ef5c9fad --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/RelativePath.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Validator\ViolationMapper; + +use Symfony\Component\Form\FormInterface; +use Symfony\Component\PropertyAccess\PropertyPath; + +/** + * @author Bernhard Schussek + */ +class RelativePath extends PropertyPath +{ + /** + * @var FormInterface + */ + private $root; + + /** + * @param FormInterface $root + * @param string $propertyPath + */ + public function __construct(FormInterface $root, $propertyPath) + { + parent::__construct($propertyPath); + + $this->root = $root; + } + + /** + * @return FormInterface + */ + public function getRoot() + { + return $this->root; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php new file mode 100644 index 00000000..8a7636c7 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php @@ -0,0 +1,299 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Validator\ViolationMapper; + +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\Util\InheritDataAwareIterator; +use Symfony\Component\PropertyAccess\PropertyPathIterator; +use Symfony\Component\PropertyAccess\PropertyPathBuilder; +use Symfony\Component\PropertyAccess\PropertyPathIteratorInterface; +use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationPathIterator; +use Symfony\Component\Form\FormError; +use Symfony\Component\Validator\ConstraintViolation; + +/** + * @author Bernhard Schussek + */ +class ViolationMapper implements ViolationMapperInterface +{ + /** + * @var Boolean + */ + private $allowNonSynchronized; + + /** + * {@inheritdoc} + */ + public function mapViolation(ConstraintViolation $violation, FormInterface $form, $allowNonSynchronized = false) + { + $this->allowNonSynchronized = $allowNonSynchronized; + + // The scope is the currently found most specific form that + // an error should be mapped to. After setting the scope, the + // mapper will try to continue to find more specific matches in + // the children of scope. If it cannot, the error will be + // mapped to this scope. + $scope = null; + + $violationPath = null; + $relativePath = null; + $match = false; + + // Don't create a ViolationPath instance for empty property paths + if (strlen($violation->getPropertyPath()) > 0) { + $violationPath = new ViolationPath($violation->getPropertyPath()); + $relativePath = $this->reconstructPath($violationPath, $form); + } + + // This case happens if the violation path is empty and thus + // the violation should be mapped to the root form + if (null === $violationPath) { + $scope = $form; + } + + // In general, mapping happens from the root form to the leaf forms + // First, the rules of the root form are applied to determine + // the subsequent descendant. The rules of this descendant are then + // applied to find the next and so on, until we have found the + // most specific form that matches the violation. + + // If any of the forms found in this process is not synchronized, + // mapping is aborted. Non-synchronized forms could not reverse + // transform the value entered by the user, thus any further violations + // caused by the (invalid) reverse transformed value should be + // ignored. + + if (null !== $relativePath) { + // Set the scope to the root of the relative path + // This root will usually be $form. If the path contains + // an unmapped form though, the last unmapped form found + // will be the root of the path. + $scope = $relativePath->getRoot(); + $it = new PropertyPathIterator($relativePath); + + while ($this->acceptsErrors($scope) && null !== ($child = $this->matchChild($scope, $it))) { + $scope = $child; + $it->next(); + $match = true; + } + } + + // This case happens if an error happened in the data under a + // form inheriting its parent data that does not match any of the + // children of that form. + if (null !== $violationPath && !$match) { + // If we could not map the error to anything more specific + // than the root element, map it to the innermost directly + // mapped form of the violation path + // e.g. "children[foo].children[bar].data.baz" + // Here the innermost directly mapped child is "bar" + + $scope = $form; + $it = new ViolationPathIterator($violationPath); + + // Note: acceptsErrors() will always return true for forms inheriting + // their parent data, because these forms can never be non-synchronized + // (they don't do any data transformation on their own) + while ($this->acceptsErrors($scope) && $it->valid() && $it->mapsForm()) { + if (!$scope->has($it->current())) { + // Break if we find a reference to a non-existing child + break; + } + + $scope = $scope->get($it->current()); + $it->next(); + } + } + + // Follow dot rules until we have the final target + $mapping = $scope->getConfig()->getOption('error_mapping'); + + while ($this->acceptsErrors($scope) && isset($mapping['.'])) { + $dotRule = new MappingRule($scope, '.', $mapping['.']); + $scope = $dotRule->getTarget(); + $mapping = $scope->getConfig()->getOption('error_mapping'); + } + + // Only add the error if the form is synchronized + if ($this->acceptsErrors($scope)) { + $scope->addError(new FormError( + $violation->getMessage(), + $violation->getMessageTemplate(), + $violation->getMessageParameters(), + $violation->getMessagePluralization() + )); + } + } + + /** + * Tries to match the beginning of the property path at the + * current position against the children of the scope. + * + * If a matching child is found, it is returned. Otherwise + * null is returned. + * + * @param FormInterface $form The form to search. + * @param PropertyPathIteratorInterface $it The iterator at its current position. + * + * @return null|FormInterface The found match or null. + */ + private function matchChild(FormInterface $form, PropertyPathIteratorInterface $it) + { + // Remember at what property path underneath "data" + // we are looking. Check if there is a child with that + // path, otherwise increase path by one more piece + $chunk = ''; + $foundChild = null; + $foundAtIndex = 0; + + // Construct mapping rules for the given form + $rules = array(); + + foreach ($form->getConfig()->getOption('error_mapping') as $propertyPath => $targetPath) { + // Dot rules are considered at the very end + if ('.' !== $propertyPath) { + $rules[] = new MappingRule($form, $propertyPath, $targetPath); + } + } + + // Skip forms inheriting their parent data when iterating the children + $childIterator = new \RecursiveIteratorIterator( + new InheritDataAwareIterator($form->all()) + ); + + // Make the path longer until we find a matching child + while (true) { + if (!$it->valid()) { + return null; + } + + if ($it->isIndex()) { + $chunk .= '['.$it->current().']'; + } else { + $chunk .= ('' === $chunk ? '' : '.').$it->current(); + } + + // Test mapping rules as long as we have any + foreach ($rules as $key => $rule) { + /* @var MappingRule $rule */ + + // Mapping rule matches completely, terminate. + if (null !== ($form = $rule->match($chunk))) { + return $form; + } + + // Keep only rules that have $chunk as prefix + if (!$rule->isPrefix($chunk)) { + unset($rules[$key]); + } + } + + // Test children unless we already found one + if (null === $foundChild) { + foreach ($childIterator as $child) { + /* @var FormInterface $child */ + $childPath = (string) $child->getPropertyPath(); + + // Child found, mark as return value + if ($chunk === $childPath) { + $foundChild = $child; + $foundAtIndex = $it->key(); + } + } + } + + // Add element to the chunk + $it->next(); + + // If we reached the end of the path or if there are no + // more matching mapping rules, return the found child + if (null !== $foundChild && (!$it->valid() || count($rules) === 0)) { + // Reset index in case we tried to find mapping + // rules further down the path + $it->seek($foundAtIndex); + + return $foundChild; + } + } + + return null; + } + + /** + * Reconstructs a property path from a violation path and a form tree. + * + * @param ViolationPath $violationPath The violation path. + * @param FormInterface $origin The root form of the tree. + * + * @return RelativePath The reconstructed path. + */ + private function reconstructPath(ViolationPath $violationPath, FormInterface $origin) + { + $propertyPathBuilder = new PropertyPathBuilder($violationPath); + $it = $violationPath->getIterator(); + $scope = $origin; + + // Remember the current index in the builder + $i = 0; + + // Expand elements that map to a form (like "children[address]") + for ($it->rewind(); $it->valid() && $it->mapsForm(); $it->next()) { + if (!$scope->has($it->current())) { + // Scope relates to a form that does not exist + // Bail out + break; + } + + // Process child form + $scope = $scope->get($it->current()); + + if ($scope->getConfig()->getInheritData()) { + // Form inherits its parent data + // Cut the piece out of the property path and proceed + $propertyPathBuilder->remove($i); + } elseif (!$scope->getConfig()->getMapped()) { + // Form is not mapped + // Set the form as new origin and strip everything + // we have so far in the path + $origin = $scope; + $propertyPathBuilder->remove(0, $i + 1); + $i = 0; + } else { + /* @var \Symfony\Component\PropertyAccess\PropertyPathInterface $propertyPath */ + $propertyPath = $scope->getPropertyPath(); + + if (null === $propertyPath) { + // Property path of a mapped form is null + // Should not happen, bail out + break; + } + + $propertyPathBuilder->replace($i, 1, $propertyPath); + $i += $propertyPath->getLength(); + } + } + + $finalPath = $propertyPathBuilder->getPropertyPath(); + + return null !== $finalPath ? new RelativePath($origin, $finalPath) : null; + } + + /** + * @param FormInterface $form + * + * @return Boolean + */ + private function acceptsErrors(FormInterface $form) + { + return $this->allowNonSynchronized || $form->isSynchronized(); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapperInterface.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapperInterface.php new file mode 100644 index 00000000..eb8907f1 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapperInterface.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Validator\ViolationMapper; + +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Validator\ConstraintViolation; + +/** + * @author Bernhard Schussek + */ +interface ViolationMapperInterface +{ + /** + * Maps a constraint violation to a form in the form tree under + * the given form. + * + * @param ConstraintViolation $violation The violation to map. + * @param FormInterface $form The root form of the tree + * to map it to. + * @param Boolean $allowNonSynchronized Whether to allow + * mapping to non-synchronized forms. + */ + public function mapViolation(ConstraintViolation $violation, FormInterface $form, $allowNonSynchronized = false); +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php new file mode 100644 index 00000000..06d09195 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php @@ -0,0 +1,250 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Validator\ViolationMapper; + +use Symfony\Component\Form\Exception\OutOfBoundsException; +use Symfony\Component\PropertyAccess\PropertyPath; +use Symfony\Component\PropertyAccess\PropertyPathInterface; + +/** + * @author Bernhard Schussek + */ +class ViolationPath implements \IteratorAggregate, PropertyPathInterface +{ + /** + * @var array + */ + private $elements = array(); + + /** + * @var array + */ + private $isIndex = array(); + + /** + * @var array + */ + private $mapsForm = array(); + + /** + * @var string + */ + private $pathAsString = ''; + + /** + * @var integer + */ + private $length = 0; + + /** + * Creates a new violation path from a string. + * + * @param string $violationPath The property path of a {@link ConstraintViolation} + * object. + */ + public function __construct($violationPath) + { + $path = new PropertyPath($violationPath); + $elements = $path->getElements(); + $data = false; + + for ($i = 0, $l = count($elements); $i < $l; ++$i) { + if (!$data) { + // The element "data" has not yet been passed + if ('children' === $elements[$i] && $path->isProperty($i)) { + // Skip element "children" + ++$i; + + // Next element must exist and must be an index + // Otherwise consider this the end of the path + if ($i >= $l || !$path->isIndex($i)) { + break; + } + + $this->elements[] = $elements[$i]; + $this->isIndex[] = true; + $this->mapsForm[] = true; + } elseif ('data' === $elements[$i] && $path->isProperty($i)) { + // Skip element "data" + ++$i; + + // End of path + if ($i >= $l) { + break; + } + + $this->elements[] = $elements[$i]; + $this->isIndex[] = $path->isIndex($i); + $this->mapsForm[] = false; + $data = true; + } else { + // Neither "children" nor "data" property found + // Consider this the end of the path + break; + } + } else { + // Already after the "data" element + // Pick everything as is + $this->elements[] = $elements[$i]; + $this->isIndex[] = $path->isIndex($i); + $this->mapsForm[] = false; + } + } + + $this->length = count($this->elements); + + $this->buildString(); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return $this->pathAsString; + } + + /** + * {@inheritdoc} + */ + public function getLength() + { + return $this->length; + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + if ($this->length <= 1) { + return null; + } + + $parent = clone $this; + + --$parent->length; + array_pop($parent->elements); + array_pop($parent->isIndex); + array_pop($parent->mapsForm); + + $parent->buildString(); + + return $parent; + } + + /** + * {@inheritdoc} + */ + public function getElements() + { + return $this->elements; + } + + /** + * {@inheritdoc} + */ + public function getElement($index) + { + if (!isset($this->elements[$index])) { + throw new OutOfBoundsException(sprintf('The index %s is not within the violation path', $index)); + } + + return $this->elements[$index]; + } + + /** + * {@inheritdoc} + */ + public function isProperty($index) + { + if (!isset($this->isIndex[$index])) { + throw new OutOfBoundsException(sprintf('The index %s is not within the violation path', $index)); + } + + return !$this->isIndex[$index]; + } + + /** + * {@inheritdoc} + */ + public function isIndex($index) + { + if (!isset($this->isIndex[$index])) { + throw new OutOfBoundsException(sprintf('The index %s is not within the violation path', $index)); + } + + return $this->isIndex[$index]; + } + + /** + * Returns whether an element maps directly to a form. + * + * Consider the following violation path: + * + * + * children[address].children[office].data.street + * + * + * In this example, "address" and "office" map to forms, while + * "street does not. + * + * @param integer $index The element index. + * + * @return Boolean Whether the element maps to a form. + * + * @throws OutOfBoundsException If the offset is invalid. + */ + public function mapsForm($index) + { + if (!isset($this->mapsForm[$index])) { + throw new OutOfBoundsException(sprintf('The index %s is not within the violation path', $index)); + } + + return $this->mapsForm[$index]; + } + + /** + * Returns a new iterator for this path + * + * @return ViolationPathIterator + */ + public function getIterator() + { + return new ViolationPathIterator($this); + } + + /** + * Builds the string representation from the elements. + */ + private function buildString() + { + $this->pathAsString = ''; + $data = false; + + foreach ($this->elements as $index => $element) { + if ($this->mapsForm[$index]) { + $this->pathAsString .= ".children[$element]"; + } elseif (!$data) { + $this->pathAsString .= '.data'.($this->isIndex[$index] ? "[$element]" : ".$element"); + $data = true; + } else { + $this->pathAsString .= $this->isIndex[$index] ? "[$element]" : ".$element"; + } + } + + if ('' !== $this->pathAsString) { + // remove leading dot + $this->pathAsString = substr($this->pathAsString, 1); + } + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPathIterator.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPathIterator.php new file mode 100644 index 00000000..50baa453 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPathIterator.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Validator\ViolationMapper; + +use Symfony\Component\PropertyAccess\PropertyPathIterator; + +/** + * @author Bernhard Schussek + */ +class ViolationPathIterator extends PropertyPathIterator +{ + public function __construct(ViolationPath $violationPath) + { + parent::__construct($violationPath); + } + + public function mapsForm() + { + return $this->path->mapsForm($this->key()); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Form.php b/vendor/symfony/form/Symfony/Component/Form/Form.php new file mode 100644 index 00000000..35135274 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Form.php @@ -0,0 +1,1046 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\Form\Exception\RuntimeException; +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\Form\Exception\AlreadySubmittedException; +use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\Exception\LogicException; +use Symfony\Component\Form\Exception\OutOfBoundsException; +use Symfony\Component\Form\Util\FormUtil; +use Symfony\Component\Form\Util\InheritDataAwareIterator; +use Symfony\Component\PropertyAccess\PropertyPath; + +/** + * Form represents a form. + * + * To implement your own form fields, you need to have a thorough understanding + * of the data flow within a form. A form stores its data in three different + * representations: + * + * (1) the "model" format required by the form's object + * (2) the "normalized" format for internal processing + * (3) the "view" format used for display + * + * A date field, for example, may store a date as "Y-m-d" string (1) in the + * object. To facilitate processing in the field, this value is normalized + * to a DateTime object (2). In the HTML representation of your form, a + * localized string (3) is presented to and modified by the user. + * + * In most cases, format (1) and format (2) will be the same. For example, + * a checkbox field uses a Boolean value for both internal processing and + * storage in the object. In these cases you simply need to set a value + * transformer to convert between formats (2) and (3). You can do this by + * calling addViewTransformer(). + * + * In some cases though it makes sense to make format (1) configurable. To + * demonstrate this, let's extend our above date field to store the value + * either as "Y-m-d" string or as timestamp. Internally we still want to + * use a DateTime object for processing. To convert the data from string/integer + * to DateTime you can set a normalization transformer by calling + * addNormTransformer(). The normalized data is then converted to the displayed + * data as described before. + * + * The conversions (1) -> (2) -> (3) use the transform methods of the transformers. + * The conversions (3) -> (2) -> (1) use the reverseTransform methods of the transformers. + * + * @author Fabien Potencier + * @author Bernhard Schussek + */ +class Form implements \IteratorAggregate, FormInterface +{ + /** + * The form's configuration + * @var FormConfigInterface + */ + private $config; + + /** + * The parent of this form + * @var FormInterface + */ + private $parent; + + /** + * The children of this form + * @var FormInterface[] An array of FormInterface instances + */ + private $children = array(); + + /** + * The errors of this form + * @var FormError[] An array of FormError instances + */ + private $errors = array(); + + /** + * Whether this form was submitted + * @var Boolean + */ + private $submitted = false; + + /** + * The form data in model format + * @var mixed + */ + private $modelData; + + /** + * The form data in normalized format + * @var mixed + */ + private $normData; + + /** + * The form data in view format + * @var mixed + */ + private $viewData; + + /** + * The submitted values that don't belong to any children + * @var array + */ + private $extraData = array(); + + /** + * Whether the data in model, normalized and view format is + * synchronized. Data may not be synchronized if transformation errors + * occur. + * @var Boolean + */ + private $synchronized = true; + + /** + * Whether the form's data has been initialized. + * + * When the data is initialized with its default value, that default value + * is passed through the transformer chain in order to synchronize the + * model, normalized and view format for the first time. This is done + * lazily in order to save performance when {@link setData()} is called + * manually, making the initialization with the configured default value + * superfluous. + * + * @var Boolean + */ + private $defaultDataSet = false; + + /** + * Whether setData() is currently being called. + * @var Boolean + */ + private $lockSetData = false; + + /** + * Creates a new form based on the given configuration. + * + * @param FormConfigInterface $config The form configuration. + * + * @throws LogicException if a data mapper is not provided for a compound form + */ + public function __construct(FormConfigInterface $config) + { + // Compound forms always need a data mapper, otherwise calls to + // `setData` and `add` will not lead to the correct population of + // the child forms. + if ($config->getCompound() && !$config->getDataMapper()) { + throw new LogicException('Compound forms need a data mapper'); + } + + // If the form inherits the data from its parent, it is not necessary + // to call setData() with the default data. + if ($config->getInheritData()) { + $this->defaultDataSet = true; + } + + $this->config = $config; + } + + public function __clone() + { + foreach ($this->children as $key => $child) { + $this->children[$key] = clone $child; + } + } + + /** + * {@inheritdoc} + */ + public function getConfig() + { + return $this->config; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->config->getName(); + } + + /** + * {@inheritdoc} + */ + public function getPropertyPath() + { + if (null !== $this->config->getPropertyPath()) { + return $this->config->getPropertyPath(); + } + + if (null === $this->getName() || '' === $this->getName()) { + return null; + } + + $parent = $this->parent; + + while ($parent && $parent->getConfig()->getInheritData()) { + $parent = $parent->getParent(); + } + + if ($parent && null === $parent->getConfig()->getDataClass()) { + return new PropertyPath('['.$this->getName().']'); + } + + return new PropertyPath($this->getName()); + } + + /** + * {@inheritdoc} + */ + public function isRequired() + { + if (null === $this->parent || $this->parent->isRequired()) { + return $this->config->getRequired(); + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function isDisabled() + { + if (null === $this->parent || !$this->parent->isDisabled()) { + return $this->config->getDisabled(); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function setParent(FormInterface $parent = null) + { + if ($this->submitted) { + throw new AlreadySubmittedException('You cannot set the parent of a submitted form'); + } + + if (null !== $parent && '' === $this->config->getName()) { + throw new LogicException('A form with an empty name cannot have a parent form.'); + } + + $this->parent = $parent; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return $this->parent; + } + + /** + * {@inheritdoc} + */ + public function getRoot() + { + return $this->parent ? $this->parent->getRoot() : $this; + } + + /** + * {@inheritdoc} + */ + public function isRoot() + { + return null === $this->parent; + } + + /** + * {@inheritdoc} + */ + public function setData($modelData) + { + // If the form is submitted while disabled, it is set to submitted, but the data is not + // changed. In such cases (i.e. when the form is not initialized yet) don't + // abort this method. + if ($this->submitted && $this->defaultDataSet) { + throw new AlreadySubmittedException('You cannot change the data of a submitted form.'); + } + + // If the form inherits its parent's data, disallow data setting to + // prevent merge conflicts + if ($this->config->getInheritData()) { + throw new RuntimeException('You cannot change the data of a form inheriting its parent data.'); + } + + // Don't allow modifications of the configured data if the data is locked + if ($this->config->getDataLocked() && $modelData !== $this->config->getData()) { + return $this; + } + + if (is_object($modelData) && !$this->config->getByReference()) { + $modelData = clone $modelData; + } + + if ($this->lockSetData) { + throw new RuntimeException('A cycle was detected. Listeners to the PRE_SET_DATA event must not call setData(). You should call setData() on the FormEvent object instead.'); + } + + $this->lockSetData = true; + $dispatcher = $this->config->getEventDispatcher(); + + // Hook to change content of the data + if ($dispatcher->hasListeners(FormEvents::PRE_SET_DATA)) { + $event = new FormEvent($this, $modelData); + $dispatcher->dispatch(FormEvents::PRE_SET_DATA, $event); + $modelData = $event->getData(); + } + + // Treat data as strings unless a value transformer exists + if (!$this->config->getViewTransformers() && !$this->config->getModelTransformers() && is_scalar($modelData)) { + $modelData = (string) $modelData; + } + + // Synchronize representations - must not change the content! + $normData = $this->modelToNorm($modelData); + $viewData = $this->normToView($normData); + + // Validate if view data matches data class (unless empty) + if (!FormUtil::isEmpty($viewData)) { + $dataClass = $this->config->getDataClass(); + + $actualType = is_object($viewData) ? 'an instance of class '.get_class($viewData) : ' a(n) '.gettype($viewData); + + if (null === $dataClass && is_object($viewData) && !$viewData instanceof \ArrayAccess) { + $expectedType = 'scalar, array or an instance of \ArrayAccess'; + + throw new LogicException( + 'The form\'s view data is expected to be of type '.$expectedType.', ' . + 'but is '.$actualType.'. You ' . + 'can avoid this error by setting the "data_class" option to ' . + '"'.get_class($viewData).'" or by adding a view transformer ' . + 'that transforms '.$actualType.' to '.$expectedType.'.' + ); + } + + if (null !== $dataClass && !$viewData instanceof $dataClass) { + throw new LogicException( + 'The form\'s view data is expected to be an instance of class ' . + $dataClass.', but is '. $actualType.'. You can avoid this error ' . + 'by setting the "data_class" option to null or by adding a view ' . + 'transformer that transforms '.$actualType.' to an instance of ' . + $dataClass.'.' + ); + } + } + + $this->modelData = $modelData; + $this->normData = $normData; + $this->viewData = $viewData; + $this->defaultDataSet = true; + $this->lockSetData = false; + + // It is not necessary to invoke this method if the form doesn't have children, + // even if the form is compound. + if (count($this->children) > 0) { + // Update child forms from the data + $childrenIterator = new InheritDataAwareIterator($this->children); + $childrenIterator = new \RecursiveIteratorIterator($childrenIterator); + $this->config->getDataMapper()->mapDataToForms($viewData, $childrenIterator); + } + + if ($dispatcher->hasListeners(FormEvents::POST_SET_DATA)) { + $event = new FormEvent($this, $modelData); + $dispatcher->dispatch(FormEvents::POST_SET_DATA, $event); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getData() + { + if ($this->config->getInheritData()) { + if (!$this->parent) { + throw new RuntimeException('The form is configured to inherit its parent\'s data, but does not have a parent.'); + } + + return $this->parent->getData(); + } + + if (!$this->defaultDataSet) { + $this->setData($this->config->getData()); + } + + return $this->modelData; + } + + /** + * {@inheritdoc} + */ + public function getNormData() + { + if ($this->config->getInheritData()) { + if (!$this->parent) { + throw new RuntimeException('The form is configured to inherit its parent\'s data, but does not have a parent.'); + } + + return $this->parent->getNormData(); + } + + if (!$this->defaultDataSet) { + $this->setData($this->config->getData()); + } + + return $this->normData; + } + + /** + * {@inheritdoc} + */ + public function getViewData() + { + if ($this->config->getInheritData()) { + if (!$this->parent) { + throw new RuntimeException('The form is configured to inherit its parent\'s data, but does not have a parent.'); + } + + return $this->parent->getViewData(); + } + + if (!$this->defaultDataSet) { + $this->setData($this->config->getData()); + } + + return $this->viewData; + } + + /** + * {@inheritdoc} + */ + public function getExtraData() + { + return $this->extraData; + } + + /** + * {@inheritdoc} + */ + public function initialize() + { + if (null !== $this->parent) { + throw new RuntimeException('Only root forms should be initialized.'); + } + + // Guarantee that the *_SET_DATA events have been triggered once the + // form is initialized. This makes sure that dynamically added or + // removed fields are already visible after initialization. + if (!$this->defaultDataSet) { + $this->setData($this->config->getData()); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function handleRequest($request = null) + { + $this->config->getRequestHandler()->handleRequest($this, $request); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function submit($submittedData, $clearMissing = true) + { + if ($this->submitted) { + throw new AlreadySubmittedException('A form can only be submitted once'); + } + + // Initialize errors in the very beginning so that we don't lose any + // errors added during listeners + $this->errors = array(); + + // Obviously, a disabled form should not change its data upon submission. + if ($this->isDisabled()) { + $this->submitted = true; + + return $this; + } + + // The data must be initialized if it was not initialized yet. + // This is necessary to guarantee that the *_SET_DATA listeners + // are always invoked before submit() takes place. + if (!$this->defaultDataSet) { + $this->setData($this->config->getData()); + } + + // Treat false as NULL to support binding false to checkboxes. + // Don't convert NULL to a string here in order to determine later + // whether an empty value has been submitted or whether no value has + // been submitted at all. This is important for processing checkboxes + // and radio buttons with empty values. + if (false === $submittedData) { + $submittedData = null; + } elseif (is_scalar($submittedData)) { + $submittedData = (string) $submittedData; + } + + $dispatcher = $this->config->getEventDispatcher(); + + // Hook to change content of the data submitted by the browser + if ($dispatcher->hasListeners(FormEvents::PRE_SUBMIT)) { + $event = new FormEvent($this, $submittedData); + $dispatcher->dispatch(FormEvents::PRE_SUBMIT, $event); + $submittedData = $event->getData(); + } + + // Check whether the form is compound. + // This check is preferable over checking the number of children, + // since forms without children may also be compound. + // (think of empty collection forms) + if ($this->config->getCompound()) { + if (!is_array($submittedData)) { + $submittedData = array(); + } + + foreach ($this->children as $name => $child) { + if (isset($submittedData[$name]) || $clearMissing) { + $child->submit(isset($submittedData[$name]) ? $submittedData[$name] : null, $clearMissing); + unset($submittedData[$name]); + } + } + + $this->extraData = $submittedData; + } + + // Forms that inherit their parents' data also are not processed, + // because then it would be too difficult to merge the changes in + // the child and the parent form. Instead, the parent form also takes + // changes in the grandchildren (i.e. children of the form that inherits + // its parent's data) into account. + // (see InheritDataAwareIterator below) + if ($this->config->getInheritData()) { + $this->submitted = true; + + // When POST_SUBMIT is reached, the data is not yet updated, so pass + // NULL to prevent hard-to-debug bugs. + $dataForPostSubmit = null; + } else { + // If the form is compound, the default data in view format + // is reused. The data of the children is merged into this + // default data using the data mapper. + // If the form is not compound, the submitted data is also the data in view format. + $viewData = $this->config->getCompound() ? $this->viewData : $submittedData; + + if (FormUtil::isEmpty($viewData)) { + $emptyData = $this->config->getEmptyData(); + + if ($emptyData instanceof \Closure) { + /* @var \Closure $emptyData */ + $emptyData = $emptyData($this, $viewData); + } + + $viewData = $emptyData; + } + + // Merge form data from children into existing view data + // It is not necessary to invoke this method if the form has no children, + // even if it is compound. + if (count($this->children) > 0) { + // Use InheritDataAwareIterator to process children of + // descendants that inherit this form's data. + // These descendants will not be submitted normally (see the check + // for $this->config->getInheritData() above) + $childrenIterator = new InheritDataAwareIterator($this->children); + $childrenIterator = new \RecursiveIteratorIterator($childrenIterator); + $this->config->getDataMapper()->mapFormsToData($childrenIterator, $viewData); + } + + $modelData = null; + $normData = null; + + try { + // Normalize data to unified representation + $normData = $this->viewToNorm($viewData); + + // Hook to change content of the data into the normalized + // representation + if ($dispatcher->hasListeners(FormEvents::SUBMIT)) { + $event = new FormEvent($this, $normData); + $dispatcher->dispatch(FormEvents::SUBMIT, $event); + $normData = $event->getData(); + } + + // Synchronize representations - must not change the content! + $modelData = $this->normToModel($normData); + $viewData = $this->normToView($normData); + } catch (TransformationFailedException $e) { + $this->synchronized = false; + } + + $this->submitted = true; + $this->modelData = $modelData; + $this->normData = $normData; + $this->viewData = $viewData; + + $dataForPostSubmit = $viewData; + } + + if ($dispatcher->hasListeners(FormEvents::POST_SUBMIT)) { + $event = new FormEvent($this, $dataForPostSubmit); + $dispatcher->dispatch(FormEvents::POST_SUBMIT, $event); + } + + return $this; + } + + /** + * Alias of {@link submit()}. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link submit()} instead. + */ + public function bind($submittedData) + { + return $this->submit($submittedData); + } + + /** + * {@inheritdoc} + */ + public function addError(FormError $error) + { + if ($this->parent && $this->config->getErrorBubbling()) { + $this->parent->addError($error); + } else { + $this->errors[] = $error; + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function isSubmitted() + { + return $this->submitted; + } + + /** + * Alias of {@link isSubmitted()}. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link isSubmitted()} instead. + */ + public function isBound() + { + return $this->submitted; + } + + /** + * {@inheritdoc} + */ + public function isSynchronized() + { + return $this->synchronized; + } + + /** + * {@inheritdoc} + */ + public function isEmpty() + { + foreach ($this->children as $child) { + if (!$child->isEmpty()) { + return false; + } + } + + return FormUtil::isEmpty($this->modelData) || + // arrays, countables + 0 === count($this->modelData) || + // traversables that are not countable + ($this->modelData instanceof \Traversable && 0 === iterator_count($this->modelData)); + } + + /** + * {@inheritdoc} + */ + public function isValid() + { + if (!$this->submitted) { + return false; + } + + if (count($this->errors) > 0) { + return false; + } + + if (!$this->isDisabled()) { + foreach ($this->children as $child) { + if (!$child->isValid()) { + return false; + } + } + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function getErrors() + { + return $this->errors; + } + + /** + * Returns a string representation of all form errors (including children errors). + * + * This method should only be used to help debug a form. + * + * @param integer $level The indentation level (used internally) + * + * @return string A string representation of all errors + */ + public function getErrorsAsString($level = 0) + { + $errors = ''; + foreach ($this->errors as $error) { + $errors .= str_repeat(' ', $level).'ERROR: '.$error->getMessage()."\n"; + } + + foreach ($this->children as $key => $child) { + $errors .= str_repeat(' ', $level).$key.":\n"; + if ($err = $child->getErrorsAsString($level + 4)) { + $errors .= $err; + } else { + $errors .= str_repeat(' ', $level + 4)."No errors\n"; + } + } + + return $errors; + } + + /** + * {@inheritdoc} + */ + public function all() + { + return $this->children; + } + + /** + * {@inheritdoc} + */ + public function add($child, $type = null, array $options = array()) + { + if ($this->submitted) { + throw new AlreadySubmittedException('You cannot add children to a submitted form'); + } + + if (!$this->config->getCompound()) { + throw new LogicException('You cannot add children to a simple form. Maybe you should set the option "compound" to true?'); + } + + // Obtain the view data + $viewData = null; + + // If setData() is currently being called, there is no need to call + // mapDataToForms() here, as mapDataToForms() is called at the end + // of setData() anyway. Not doing this check leads to an endless + // recursion when initializing the form lazily and an event listener + // (such as ResizeFormListener) adds fields depending on the data: + // + // * setData() is called, the form is not initialized yet + // * add() is called by the listener (setData() is not complete, so + // the form is still not initialized) + // * getViewData() is called + // * setData() is called since the form is not initialized yet + // * ... endless recursion ... + // + // Also skip data mapping if setData() has not been called yet. + // setData() will be called upon form initialization and data mapping + // will take place by then. + if (!$this->lockSetData && $this->defaultDataSet && !$this->config->getInheritData()) { + $viewData = $this->getViewData(); + } + + if (!$child instanceof FormInterface) { + if (!is_string($child) && !is_int($child)) { + throw new UnexpectedTypeException($child, 'string, integer or Symfony\Component\Form\FormInterface'); + } + + if (null !== $type && !is_string($type) && !$type instanceof FormTypeInterface) { + throw new UnexpectedTypeException($type, 'string or Symfony\Component\Form\FormTypeInterface'); + } + + // Never initialize child forms automatically + $options['auto_initialize'] = false; + + if (null === $type) { + $child = $this->config->getFormFactory()->createForProperty($this->config->getDataClass(), $child, null, $options); + } else { + $child = $this->config->getFormFactory()->createNamed($child, $type, null, $options); + } + } elseif ($child->getConfig()->getAutoInitialize()) { + throw new RuntimeException(sprintf( + 'Automatic initialization is only supported on root forms. You '. + 'should set the "auto_initialize" option to false on the field "%s".', + $child->getName() + )); + } + + $this->children[$child->getName()] = $child; + + $child->setParent($this); + + if (!$this->lockSetData && $this->defaultDataSet && !$this->config->getInheritData()) { + $childrenIterator = new InheritDataAwareIterator(array($child)); + $childrenIterator = new \RecursiveIteratorIterator($childrenIterator); + $this->config->getDataMapper()->mapDataToForms($viewData, $childrenIterator); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function remove($name) + { + if ($this->submitted) { + throw new AlreadySubmittedException('You cannot remove children from a submitted form'); + } + + if (isset($this->children[$name])) { + $this->children[$name]->setParent(null); + + unset($this->children[$name]); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function has($name) + { + return isset($this->children[$name]); + } + + /** + * {@inheritdoc} + */ + public function get($name) + { + if (isset($this->children[$name])) { + return $this->children[$name]; + } + + throw new OutOfBoundsException(sprintf('Child "%s" does not exist.', $name)); + } + + /** + * Returns whether a child with the given name exists (implements the \ArrayAccess interface). + * + * @param string $name The name of the child + * + * @return Boolean + */ + public function offsetExists($name) + { + return $this->has($name); + } + + /** + * Returns the child with the given name (implements the \ArrayAccess interface). + * + * @param string $name The name of the child + * + * @return FormInterface The child form + * + * @throws \OutOfBoundsException If the named child does not exist. + */ + public function offsetGet($name) + { + return $this->get($name); + } + + /** + * Adds a child to the form (implements the \ArrayAccess interface). + * + * @param string $name Ignored. The name of the child is used. + * @param FormInterface $child The child to be added. + * + * @throws AlreadySubmittedException If the form has already been submitted. + * @throws LogicException When trying to add a child to a non-compound form. + * + * @see self::add() + */ + public function offsetSet($name, $child) + { + $this->add($child); + } + + /** + * Removes the child with the given name from the form (implements the \ArrayAccess interface). + * + * @param string $name The name of the child to remove + * + * @throws AlreadySubmittedException If the form has already been submitted. + */ + public function offsetUnset($name) + { + $this->remove($name); + } + + /** + * Returns the iterator for this group. + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->children); + } + + /** + * Returns the number of form children (implements the \Countable interface). + * + * @return integer The number of embedded form children + */ + public function count() + { + return count($this->children); + } + + /** + * {@inheritdoc} + */ + public function createView(FormView $parent = null) + { + if (null === $parent && $this->parent) { + $parent = $this->parent->createView(); + } + + return $this->config->getType()->createView($this, $parent); + } + + /** + * Normalizes the value if a normalization transformer is set. + * + * @param mixed $value The value to transform + * + * @return mixed + */ + private function modelToNorm($value) + { + foreach ($this->config->getModelTransformers() as $transformer) { + $value = $transformer->transform($value); + } + + return $value; + } + + /** + * Reverse transforms a value if a normalization transformer is set. + * + * @param string $value The value to reverse transform + * + * @return mixed + */ + private function normToModel($value) + { + $transformers = $this->config->getModelTransformers(); + + for ($i = count($transformers) - 1; $i >= 0; --$i) { + $value = $transformers[$i]->reverseTransform($value); + } + + return $value; + } + + /** + * Transforms the value if a value transformer is set. + * + * @param mixed $value The value to transform + * + * @return mixed + */ + private function normToView($value) + { + // Scalar values should be converted to strings to + // facilitate differentiation between empty ("") and zero (0). + // Only do this for simple forms, as the resulting value in + // compound forms is passed to the data mapper and thus should + // not be converted to a string before. + if (!$this->config->getViewTransformers() && !$this->config->getCompound()) { + return null === $value || is_scalar($value) ? (string) $value : $value; + } + + foreach ($this->config->getViewTransformers() as $transformer) { + $value = $transformer->transform($value); + } + + return $value; + } + + /** + * Reverse transforms a value if a value transformer is set. + * + * @param string $value The value to reverse transform + * + * @return mixed + */ + private function viewToNorm($value) + { + $transformers = $this->config->getViewTransformers(); + + if (!$transformers) { + return '' === $value ? null : $value; + } + + for ($i = count($transformers) - 1; $i >= 0; --$i) { + $value = $transformers[$i]->reverseTransform($value); + } + + return $value; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/FormBuilder.php b/vendor/symfony/form/Symfony/Component/Form/FormBuilder.php new file mode 100644 index 00000000..2caefe48 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/FormBuilder.php @@ -0,0 +1,275 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\Form\Exception\BadMethodCallException; +use Symfony\Component\Form\Exception\InvalidArgumentException; +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * A builder for creating {@link Form} instances. + * + * @author Bernhard Schussek + */ +class FormBuilder extends FormConfigBuilder implements \IteratorAggregate, FormBuilderInterface +{ + /** + * The children of the form builder. + * + * @var FormBuilderInterface[] + */ + private $children = array(); + + /** + * The data of children who haven't been converted to form builders yet. + * + * @var array + */ + private $unresolvedChildren = array(); + + /** + * Creates a new form builder. + * + * @param string $name + * @param string $dataClass + * @param EventDispatcherInterface $dispatcher + * @param FormFactoryInterface $factory + * @param array $options + */ + public function __construct($name, $dataClass, EventDispatcherInterface $dispatcher, FormFactoryInterface $factory, array $options = array()) + { + parent::__construct($name, $dataClass, $dispatcher, $options); + + $this->setFormFactory($factory); + } + + /** + * {@inheritdoc} + */ + public function add($child, $type = null, array $options = array()) + { + if ($this->locked) { + throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + if ($child instanceof self) { + $this->children[$child->getName()] = $child; + + // In case an unresolved child with the same name exists + unset($this->unresolvedChildren[$child->getName()]); + + return $this; + } + + if (!is_string($child) && !is_int($child)) { + throw new UnexpectedTypeException($child, 'string, integer or Symfony\Component\Form\FormBuilder'); + } + + if (null !== $type && !is_string($type) && !$type instanceof FormTypeInterface) { + throw new UnexpectedTypeException($type, 'string or Symfony\Component\Form\FormTypeInterface'); + } + + // Add to "children" to maintain order + $this->children[$child] = null; + $this->unresolvedChildren[$child] = array( + 'type' => $type, + 'options' => $options, + ); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function create($name, $type = null, array $options = array()) + { + 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 = 'text'; + } + + if (null !== $type) { + return $this->getFormFactory()->createNamedBuilder($name, $type, null, $options); + } + + return $this->getFormFactory()->createBuilderForProperty($this->getDataClass(), $name, null, $options); + } + + /** + * {@inheritdoc} + */ + public function get($name) + { + 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->children[$name])) { + return $this->children[$name]; + } + + throw new InvalidArgumentException(sprintf('The child with the name "%s" does not exist.', $name)); + } + + /** + * {@inheritdoc} + */ + public function remove($name) + { + 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]); + + if (array_key_exists($name, $this->children)) { + unset($this->children[$name]); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function has($name) + { + 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 true; + } + + if (isset($this->children[$name])) { + return true; + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function all() + { + if ($this->locked) { + throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->resolveChildren(); + + return $this->children; + } + + /** + * {@inheritdoc} + */ + public function count() + { + 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); + } + + /** + * {@inheritdoc} + */ + public function getFormConfig() + { + $config = parent::getFormConfig(); + + $config->children = array(); + $config->unresolvedChildren = array(); + + return $config; + } + + /** + * {@inheritdoc} + */ + public function getForm() + { + if ($this->locked) { + throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->resolveChildren(); + + $form = new Form($this->getFormConfig()); + + 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(); + } + + return $form; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + 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->children); + } + + /** + * Converts an unresolved child into a {@link FormBuilder} instance. + * + * @param string $name The name of the unresolved child. + * + * @return FormBuilder The created instance. + */ + private function resolveChild($name) + { + $info = $this->unresolvedChildren[$name]; + $child = $this->create($name, $info['type'], $info['options']); + $this->children[$name] = $child; + unset($this->unresolvedChildren[$name]); + + return $child; + } + + /** + * Converts all unresolved children into {@link FormBuilder} instances. + */ + private function resolveChildren() + { + foreach ($this->unresolvedChildren as $name => $info) { + $this->children[$name] = $this->create($name, $info['type'], $info['options']); + } + + $this->unresolvedChildren = array(); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/FormBuilderInterface.php b/vendor/symfony/form/Symfony/Component/Form/FormBuilderInterface.php new file mode 100644 index 00000000..1dc4a64e --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/FormBuilderInterface.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * @author Bernhard Schussek + */ +interface FormBuilderInterface extends \Traversable, \Countable, FormConfigBuilderInterface +{ + /** + * Adds a new field to this group. A field must have a unique name within + * the group. Otherwise the existing field is overwritten. + * + * If you add a nested group, this group should also be represented in the + * object hierarchy. + * + * @param string|integer|FormBuilderInterface $child + * @param string|FormTypeInterface $type + * @param array $options + * + * @return FormBuilderInterface The builder object. + */ + public function add($child, $type = null, array $options = array()); + + /** + * Creates a form builder. + * + * @param string $name The name of the form or the name of the property + * @param string|FormTypeInterface $type The type of the form or null if name is a property + * @param array $options The options + * + * @return FormBuilderInterface The created builder. + */ + public function create($name, $type = null, array $options = array()); + + /** + * Returns a child by name. + * + * @param string $name The name of the child + * + * @return FormBuilderInterface The builder for the child + * + * @throws Exception\InvalidArgumentException if the given child does not exist + */ + public function get($name); + + /** + * Removes the field with the given name. + * + * @param string $name + * + * @return FormBuilderInterface The builder object. + */ + public function remove($name); + + /** + * Returns whether a field with the given name exists. + * + * @param string $name + * + * @return Boolean + */ + public function has($name); + + /** + * Returns the children. + * + * @return array + */ + public function all(); + + /** + * Creates the form. + * + * @return Form The form + */ + public function getForm(); +} diff --git a/vendor/symfony/form/Symfony/Component/Form/FormConfigBuilder.php b/vendor/symfony/form/Symfony/Component/Form/FormConfigBuilder.php new file mode 100644 index 00000000..1015da4f --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/FormConfigBuilder.php @@ -0,0 +1,919 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\Form\Exception\BadMethodCallException; +use Symfony\Component\Form\Exception\InvalidArgumentException; +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\PropertyAccess\PropertyPath; +use Symfony\Component\PropertyAccess\PropertyPathInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\EventDispatcher\ImmutableEventDispatcher; + +/** + * A basic form configuration. + * + * @author Bernhard Schussek + */ +class FormConfigBuilder implements FormConfigBuilderInterface +{ + /** + * Caches a globally unique {@link NativeRequestHandler} instance. + * + * @var NativeRequestHandler + */ + private static $nativeRequestProcessor; + + /** + * The accepted request methods. + * + * @var array + */ + private static $allowedMethods = array( + 'GET', + 'PUT', + 'POST', + 'DELETE', + 'PATCH' + ); + + /** + * @var Boolean + */ + protected $locked = false; + + /** + * @var EventDispatcherInterface + */ + private $dispatcher; + + /** + * @var string + */ + private $name; + + /** + * @var PropertyPathInterface + */ + private $propertyPath; + + /** + * @var Boolean + */ + private $mapped = true; + + /** + * @var Boolean + */ + private $byReference = true; + + /** + * @var Boolean + */ + private $inheritData = false; + + /** + * @var Boolean + */ + private $compound = false; + + /** + * @var ResolvedFormTypeInterface + */ + private $type; + + /** + * @var array + */ + private $viewTransformers = array(); + + /** + * @var array + */ + private $modelTransformers = array(); + + /** + * @var DataMapperInterface + */ + private $dataMapper; + + /** + * @var Boolean + */ + private $required = true; + + /** + * @var Boolean + */ + private $disabled = false; + + /** + * @var Boolean + */ + private $errorBubbling = false; + + /** + * @var mixed + */ + private $emptyData; + + /** + * @var array + */ + private $attributes = array(); + + /** + * @var mixed + */ + private $data; + + /** + * @var string + */ + private $dataClass; + + /** + * @var Boolean + */ + private $dataLocked; + + /** + * @var FormFactoryInterface + */ + private $formFactory; + + /** + * @var string + */ + private $action; + + /** + * @var string + */ + private $method = 'POST'; + + /** + * @var RequestHandlerInterface + */ + private $requestHandler; + + /** + * @var Boolean + */ + private $autoInitialize = false; + + /** + * @var array + */ + private $options; + + /** + * Creates an empty form configuration. + * + * @param string|integer $name The form name + * @param string $dataClass The class of the form's data + * @param EventDispatcherInterface $dispatcher The event dispatcher + * @param array $options The form options + * + * @throws InvalidArgumentException If the data class is not a valid class or if + * the name contains invalid characters. + */ + public function __construct($name, $dataClass, EventDispatcherInterface $dispatcher, array $options = array()) + { + self::validateName($name); + + if (null !== $dataClass && !class_exists($dataClass)) { + throw new InvalidArgumentException(sprintf('The data class "%s" is not a valid class.', $dataClass)); + } + + $this->name = (string) $name; + $this->dataClass = $dataClass; + $this->dispatcher = $dispatcher; + $this->options = $options; + } + + /** + * {@inheritdoc} + */ + public function addEventListener($eventName, $listener, $priority = 0) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->dispatcher->addListener($eventName, $listener, $priority); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addEventSubscriber(EventSubscriberInterface $subscriber) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->dispatcher->addSubscriber($subscriber); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addViewTransformer(DataTransformerInterface $viewTransformer, $forcePrepend = false) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + if ($forcePrepend) { + array_unshift($this->viewTransformers, $viewTransformer); + } else { + $this->viewTransformers[] = $viewTransformer; + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function resetViewTransformers() + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->viewTransformers = array(); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addModelTransformer(DataTransformerInterface $modelTransformer, $forceAppend = false) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + if ($forceAppend) { + $this->modelTransformers[] = $modelTransformer; + } else { + array_unshift($this->modelTransformers, $modelTransformer); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function resetModelTransformers() + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->modelTransformers = array(); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getEventDispatcher() + { + if ($this->locked && !$this->dispatcher instanceof ImmutableEventDispatcher) { + $this->dispatcher = new ImmutableEventDispatcher($this->dispatcher); + } + + return $this->dispatcher; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + /** + * {@inheritdoc} + */ + public function getPropertyPath() + { + return $this->propertyPath; + } + + /** + * {@inheritdoc} + */ + public function getMapped() + { + return $this->mapped; + } + + /** + * {@inheritdoc} + */ + public function getByReference() + { + return $this->byReference; + } + + /** + * {@inheritdoc} + */ + public function getInheritData() + { + return $this->inheritData; + } + + /** + * Alias of {@link getInheritData()}. + * + * @return FormConfigBuilder The configuration object. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link getInheritData()} instead. + */ + public function getVirtual() + { + // Uncomment this as soon as the deprecation note should be shown + // trigger_error('getVirtual() is deprecated since version 2.3 and will be removed in 3.0. Use getInheritData() instead.', E_USER_DEPRECATED); + return $this->getInheritData(); + } + + /** + * {@inheritdoc} + */ + public function getCompound() + { + return $this->compound; + } + + /** + * {@inheritdoc} + */ + public function getType() + { + return $this->type; + } + + /** + * {@inheritdoc} + */ + public function getViewTransformers() + { + return $this->viewTransformers; + } + + /** + * {@inheritdoc} + */ + public function getModelTransformers() + { + return $this->modelTransformers; + } + + /** + * {@inheritdoc} + */ + public function getDataMapper() + { + return $this->dataMapper; + } + + /** + * {@inheritdoc} + */ + public function getRequired() + { + return $this->required; + } + + /** + * {@inheritdoc} + */ + public function getDisabled() + { + return $this->disabled; + } + + /** + * {@inheritdoc} + */ + public function getErrorBubbling() + { + return $this->errorBubbling; + } + + /** + * {@inheritdoc} + */ + public function getEmptyData() + { + return $this->emptyData; + } + + /** + * {@inheritdoc} + */ + public function getAttributes() + { + return $this->attributes; + } + + /** + * {@inheritdoc} + */ + public function hasAttribute($name) + { + return array_key_exists($name, $this->attributes); + } + + /** + * {@inheritdoc} + */ + public function getAttribute($name, $default = null) + { + return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default; + } + + /** + * {@inheritdoc} + */ + public function getData() + { + return $this->data; + } + + /** + * {@inheritdoc} + */ + public function getDataClass() + { + return $this->dataClass; + } + + /** + * {@inheritdoc} + */ + public function getDataLocked() + { + return $this->dataLocked; + } + + /** + * {@inheritdoc} + */ + public function getFormFactory() + { + return $this->formFactory; + } + + /** + * {@inheritdoc} + */ + public function getAction() + { + return $this->action; + } + + /** + * {@inheritdoc} + */ + public function getMethod() + { + return $this->method; + } + + /** + * {@inheritdoc} + */ + public function getRequestHandler() + { + if (null === $this->requestHandler) { + if (null === self::$nativeRequestProcessor) { + self::$nativeRequestProcessor = new NativeRequestHandler(); + } + $this->requestHandler = self::$nativeRequestProcessor; + } + + return $this->requestHandler; + } + + /** + * {@inheritdoc} + */ + public function getAutoInitialize() + { + return $this->autoInitialize; + } + + /** + * {@inheritdoc} + */ + public function getOptions() + { + return $this->options; + } + + /** + * {@inheritdoc} + */ + public function hasOption($name) + { + return array_key_exists($name, $this->options); + } + + /** + * {@inheritdoc} + */ + public function getOption($name, $default = null) + { + return array_key_exists($name, $this->options) ? $this->options[$name] : $default; + } + + /** + * {@inheritdoc} + */ + public function setAttribute($name, $value) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->attributes[$name] = $value; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setAttributes(array $attributes) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->attributes = $attributes; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setDataMapper(DataMapperInterface $dataMapper = null) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->dataMapper = $dataMapper; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setDisabled($disabled) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->disabled = (Boolean) $disabled; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setEmptyData($emptyData) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->emptyData = $emptyData; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setErrorBubbling($errorBubbling) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->errorBubbling = null === $errorBubbling ? null : (Boolean) $errorBubbling; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setRequired($required) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->required = (Boolean) $required; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setPropertyPath($propertyPath) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + if (null !== $propertyPath && !$propertyPath instanceof PropertyPathInterface) { + $propertyPath = new PropertyPath($propertyPath); + } + + $this->propertyPath = $propertyPath; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setMapped($mapped) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->mapped = $mapped; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setByReference($byReference) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->byReference = $byReference; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setInheritData($inheritData) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->inheritData = $inheritData; + + return $this; + } + + /** + * Alias of {@link setInheritData()}. + * + * @param Boolean $inheritData Whether the form should inherit its parent's data. + * + * @return FormConfigBuilder The configuration object. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link setInheritData()} instead. + */ + public function setVirtual($inheritData) + { + // Uncomment this as soon as the deprecation note should be shown + // trigger_error('setVirtual() is deprecated since version 2.3 and will be removed in 3.0. Use setInheritData() instead.', E_USER_DEPRECATED); + + $this->setInheritData($inheritData); + } + + /** + * {@inheritdoc} + */ + public function setCompound($compound) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->compound = $compound; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setType(ResolvedFormTypeInterface $type) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->type = $type; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setData($data) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->data = $data; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setDataLocked($locked) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->dataLocked = $locked; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setFormFactory(FormFactoryInterface $formFactory) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->formFactory = $formFactory; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setAction($action) + { + if ($this->locked) { + throw new BadMethodCallException('The config builder cannot be modified anymore.'); + } + + $this->action = $action; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setMethod($method) + { + if ($this->locked) { + throw new BadMethodCallException('The config builder cannot be modified anymore.'); + } + + $upperCaseMethod = strtoupper($method); + + if (!in_array($upperCaseMethod, self::$allowedMethods)) { + throw new InvalidArgumentException(sprintf( + 'The form method is "%s", but should be one of "%s".', + $method, + implode('", "', self::$allowedMethods) + )); + } + + $this->method = $upperCaseMethod; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setRequestHandler(RequestHandlerInterface $requestHandler) + { + if ($this->locked) { + throw new BadMethodCallException('The config builder cannot be modified anymore.'); + } + + $this->requestHandler = $requestHandler; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setAutoInitialize($initialize) + { + $this->autoInitialize = (Boolean) $initialize; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getFormConfig() + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + // This method should be idempotent, so clone the builder + $config = clone $this; + $config->locked = true; + + return $config; + } + + /** + * Validates whether the given variable is a valid form name. + * + * @param string|integer $name The tested form name. + * + * @throws UnexpectedTypeException If the name is not a string or an integer. + * @throws InvalidArgumentException If the name contains invalid characters. + */ + public static function validateName($name) + { + if (null !== $name && !is_string($name) && !is_int($name)) { + throw new UnexpectedTypeException($name, 'string, integer or null'); + } + + if (!self::isValidName($name)) { + throw new InvalidArgumentException(sprintf( + 'The name "%s" contains illegal characters. Names should start with a letter, digit or underscore and only contain letters, digits, numbers, underscores ("_"), hyphens ("-") and colons (":").', + $name + )); + } + } + + /** + * Returns whether the given variable contains a valid form name. + * + * A name is accepted if it + * + * * is empty + * * starts with a letter, digit or underscore + * * contains only letters, digits, numbers, underscores ("_"), + * hyphens ("-") and colons (":") + * + * @param string $name The tested form name. + * + * @return Boolean Whether the name is valid. + */ + public static function isValidName($name) + { + return '' === $name || null === $name || preg_match('/^[a-zA-Z0-9_][a-zA-Z0-9_\-:]*$/D', $name); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/FormConfigBuilderInterface.php b/vendor/symfony/form/Symfony/Component/Form/FormConfigBuilderInterface.php new file mode 100644 index 00000000..62d12c09 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/FormConfigBuilderInterface.php @@ -0,0 +1,287 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * @author Bernhard Schussek + */ +interface FormConfigBuilderInterface extends FormConfigInterface +{ + /** + * Adds an event listener to an event on this form. + * + * @param string $eventName The name of the event to listen to. + * @param callable $listener The listener to execute. + * @param integer $priority The priority of the listener. Listeners + * with a higher priority are called before + * listeners with a lower priority. + * + * @return self The configuration object. + */ + public function addEventListener($eventName, $listener, $priority = 0); + + /** + * Adds an event subscriber for events on this form. + * + * @param EventSubscriberInterface $subscriber The subscriber to attach. + * + * @return self The configuration object. + */ + public function addEventSubscriber(EventSubscriberInterface $subscriber); + + /** + * Appends / prepends a transformer to the view transformer chain. + * + * The transform method of the transformer is used to convert data from the + * normalized to the view format. + * The reverseTransform method of the transformer is used to convert from the + * view to the normalized format. + * + * @param DataTransformerInterface $viewTransformer + * @param Boolean $forcePrepend if set to true, prepend instead of appending + * + * @return self The configuration object. + */ + public function addViewTransformer(DataTransformerInterface $viewTransformer, $forcePrepend = false); + + /** + * Clears the view transformers. + * + * @return self The configuration object. + */ + public function resetViewTransformers(); + + /** + * Prepends / appends a transformer to the normalization transformer chain. + * + * The transform method of the transformer is used to convert data from the + * model to the normalized format. + * The reverseTransform method of the transformer is used to convert from the + * normalized to the model format. + * + * @param DataTransformerInterface $modelTransformer + * @param Boolean $forceAppend if set to true, append instead of prepending + * + * @return self The configuration object. + */ + public function addModelTransformer(DataTransformerInterface $modelTransformer, $forceAppend = false); + + /** + * Clears the normalization transformers. + * + * @return self The configuration object. + */ + public function resetModelTransformers(); + + /** + * Sets the value for an attribute. + * + * @param string $name The name of the attribute + * @param string $value The value of the attribute + * + * @return self The configuration object. + */ + public function setAttribute($name, $value); + + /** + * Sets the attributes. + * + * @param array $attributes The attributes. + * + * @return self The configuration object. + */ + public function setAttributes(array $attributes); + + /** + * Sets the data mapper used by the form. + * + * @param DataMapperInterface $dataMapper + * + * @return self The configuration object. + */ + public function setDataMapper(DataMapperInterface $dataMapper = null); + + /** + * Set whether the form is disabled. + * + * @param Boolean $disabled Whether the form is disabled + * + * @return self The configuration object. + */ + public function setDisabled($disabled); + + /** + * Sets the data used for the client data when no value is submitted. + * + * @param mixed $emptyData The empty data. + * + * @return self The configuration object. + */ + public function setEmptyData($emptyData); + + /** + * Sets whether errors bubble up to the parent. + * + * @param Boolean $errorBubbling + * + * @return self The configuration object. + */ + public function setErrorBubbling($errorBubbling); + + /** + * Sets whether this field is required to be filled out when submitted. + * + * @param Boolean $required + * + * @return self The configuration object. + */ + public function setRequired($required); + + /** + * Sets the property path that the form should be mapped to. + * + * @param null|string|\Symfony\Component\PropertyAccess\PropertyPathInterface $propertyPath + * The property path or null if the path should be set + * automatically based on the form's name. + * + * @return self The configuration object. + */ + public function setPropertyPath($propertyPath); + + /** + * Sets whether the form should be mapped to an element of its + * parent's data. + * + * @param Boolean $mapped Whether the form should be mapped. + * + * @return self The configuration object. + */ + public function setMapped($mapped); + + /** + * Sets whether the form's data should be modified by reference. + * + * @param Boolean $byReference Whether the data should be + * modified by reference. + * + * @return self The configuration object. + */ + public function setByReference($byReference); + + /** + * Sets whether the form should read and write the data of its parent. + * + * @param Boolean $inheritData Whether the form should inherit its parent's data. + * + * @return self The configuration object. + */ + public function setInheritData($inheritData); + + /** + * Sets whether the form should be compound. + * + * @param Boolean $compound Whether the form should be compound. + * + * @return self The configuration object. + * + * @see FormConfigInterface::getCompound() + */ + public function setCompound($compound); + + /** + * Set the types. + * + * @param ResolvedFormTypeInterface $type The type of the form. + * + * @return self The configuration object. + */ + public function setType(ResolvedFormTypeInterface $type); + + /** + * Sets the initial data of the form. + * + * @param array $data The data of the form in application format. + * + * @return self The configuration object. + */ + public function setData($data); + + /** + * Locks the form's data to the data passed in the configuration. + * + * A form with locked data is restricted to the data passed in + * this configuration. The data can only be modified then by + * submitting the form. + * + * @param Boolean $locked Whether to lock the default data. + * + * @return self The configuration object. + */ + public function setDataLocked($locked); + + /** + * Sets the form factory used for creating new forms. + * + * @param FormFactoryInterface $formFactory The form factory. + */ + public function setFormFactory(FormFactoryInterface $formFactory); + + /** + * Sets the target URL of the form. + * + * @param string $action The target URL of the form. + * + * @return self The configuration object. + */ + public function setAction($action); + + /** + * Sets the HTTP method used by the form. + * + * @param string $method The HTTP method of the form. + * + * @return self The configuration object. + */ + public function setMethod($method); + + /** + * Sets the request handler used by the form. + * + * @param RequestHandlerInterface $requestHandler + * + * @return self The configuration object. + */ + public function setRequestHandler(RequestHandlerInterface $requestHandler); + + /** + * Sets whether the form should be initialized automatically. + * + * Should be set to true only for root forms. + * + * @param Boolean $initialize True to initialize the form automatically, + * false to suppress automatic initialization. + * In the second case, you need to call + * {@link FormInterface::initialize()} manually. + * + * @return self The configuration object. + */ + public function setAutoInitialize($initialize); + + /** + * Builds and returns the form configuration. + * + * @return FormConfigInterface + */ + public function getFormConfig(); +} diff --git a/vendor/symfony/form/Symfony/Component/Form/FormConfigInterface.php b/vendor/symfony/form/Symfony/Component/Form/FormConfigInterface.php new file mode 100644 index 00000000..576fcd81 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/FormConfigInterface.php @@ -0,0 +1,243 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * The configuration of a {@link Form} object. + * + * @author Bernhard Schussek + */ +interface FormConfigInterface +{ + /** + * Returns the event dispatcher used to dispatch form events. + * + * @return \Symfony\Component\EventDispatcher\EventDispatcherInterface The dispatcher. + */ + public function getEventDispatcher(); + + /** + * Returns the name of the form used as HTTP parameter. + * + * @return string The form name. + */ + public function getName(); + + /** + * Returns the property path that the form should be mapped to. + * + * @return null|\Symfony\Component\PropertyAccess\PropertyPathInterface The property path. + */ + public function getPropertyPath(); + + /** + * Returns whether the form should be mapped to an element of its + * parent's data. + * + * @return Boolean Whether the form is mapped. + */ + public function getMapped(); + + /** + * Returns whether the form's data should be modified by reference. + * + * @return Boolean Whether to modify the form's data by reference. + */ + public function getByReference(); + + /** + * Returns whether the form should read and write the data of its parent. + * + * @return Boolean Whether the form should inherit its parent's data. + */ + public function getInheritData(); + + /** + * Returns whether the form is compound. + * + * This property is independent of whether the form actually has + * children. A form can be compound and have no children at all, like + * for example an empty collection form. + * + * @return Boolean Whether the form is compound. + */ + public function getCompound(); + + /** + * Returns the form types used to construct the form. + * + * @return ResolvedFormTypeInterface The form's type. + */ + public function getType(); + + /** + * Returns the view transformers of the form. + * + * @return DataTransformerInterface[] An array of {@link DataTransformerInterface} instances. + */ + public function getViewTransformers(); + + /** + * Returns the model transformers of the form. + * + * @return DataTransformerInterface[] An array of {@link DataTransformerInterface} instances. + */ + public function getModelTransformers(); + + /** + * Returns the data mapper of the form. + * + * @return DataMapperInterface The data mapper. + */ + public function getDataMapper(); + + /** + * Returns whether the form is required. + * + * @return Boolean Whether the form is required. + */ + public function getRequired(); + + /** + * Returns whether the form is disabled. + * + * @return Boolean Whether the form is disabled. + */ + public function getDisabled(); + + /** + * Returns whether errors attached to the form will bubble to its parent. + * + * @return Boolean Whether errors will bubble up. + */ + public function getErrorBubbling(); + + /** + * Returns the data that should be returned when the form is empty. + * + * @return mixed The data returned if the form is empty. + */ + public function getEmptyData(); + + /** + * Returns additional attributes of the form. + * + * @return array An array of key-value combinations. + */ + public function getAttributes(); + + /** + * Returns whether the attribute with the given name exists. + * + * @param string $name The attribute name. + * + * @return Boolean Whether the attribute exists. + */ + public function hasAttribute($name); + + /** + * Returns the value of the given attribute. + * + * @param string $name The attribute name. + * @param mixed $default The value returned if the attribute does not exist. + * + * @return mixed The attribute value. + */ + public function getAttribute($name, $default = null); + + /** + * Returns the initial data of the form. + * + * @return mixed The initial form data. + */ + public function getData(); + + /** + * Returns the class of the form data or null if the data is scalar or an array. + * + * @return string The data class or null. + */ + public function getDataClass(); + + /** + * Returns whether the form's data is locked. + * + * A form with locked data is restricted to the data passed in + * this configuration. The data can only be modified then by + * submitting the form. + * + * @return Boolean Whether the data is locked. + */ + public function getDataLocked(); + + /** + * Returns the form factory used for creating new forms. + * + * @return FormFactoryInterface The form factory. + */ + public function getFormFactory(); + + /** + * Returns the target URL of the form. + * + * @return string The target URL of the form. + */ + public function getAction(); + + /** + * Returns the HTTP method used by the form. + * + * @return string The HTTP method of the form. + */ + public function getMethod(); + + /** + * Returns the request handler used by the form. + * + * @return RequestHandlerInterface The request handler. + */ + public function getRequestHandler(); + + /** + * Returns whether the form should be initialized upon creation. + * + * @return Boolean Returns true if the form should be initialized + * when created, false otherwise. + */ + public function getAutoInitialize(); + + /** + * Returns all options passed during the construction of the form. + * + * @return array The passed options. + */ + public function getOptions(); + + /** + * Returns whether a specific option exists. + * + * @param string $name The option name, + * + * @return Boolean Whether the option exists. + */ + public function hasOption($name); + + /** + * Returns the value of a specific option. + * + * @param string $name The option name. + * @param mixed $default The value returned if the option does not exist. + * + * @return mixed The option value. + */ + public function getOption($name, $default = null); +} diff --git a/vendor/symfony/form/Symfony/Component/Form/FormError.php b/vendor/symfony/form/Symfony/Component/Form/FormError.php new file mode 100644 index 00000000..343165ca --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/FormError.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * Wraps errors in forms + * + * @author Bernhard Schussek + */ +class FormError +{ + /** + * @var string + */ + private $message; + + /** + * The template for the error message + * @var string + */ + protected $messageTemplate; + + /** + * The parameters that should be substituted in the message template + * @var array + */ + protected $messageParameters; + + /** + * The value for error message pluralization + * @var integer|null + */ + protected $messagePluralization; + + /** + * Constructor + * + * Any array key in $messageParameters will be used as a placeholder in + * $messageTemplate. + * + * @param string $message The translated error message + * @param string|null $messageTemplate The template for the error message + * @param array $messageParameters The parameters that should be + * substituted in the message template. + * @param integer|null $messagePluralization The value for error message pluralization + * + * @see \Symfony\Component\Translation\Translator + */ + public function __construct($message, $messageTemplate = null, array $messageParameters = array(), $messagePluralization = null) + { + $this->message = $message; + $this->messageTemplate = $messageTemplate ?: $message; + $this->messageParameters = $messageParameters; + $this->messagePluralization = $messagePluralization; + } + + /** + * Returns the error message + * + * @return string + */ + public function getMessage() + { + return $this->message; + } + + /** + * Returns the error message template + * + * @return string + */ + public function getMessageTemplate() + { + return $this->messageTemplate; + } + + /** + * Returns the parameters to be inserted in the message template + * + * @return array + */ + public function getMessageParameters() + { + return $this->messageParameters; + } + + /** + * Returns the value for error message pluralization. + * + * @return integer|null + */ + public function getMessagePluralization() + { + return $this->messagePluralization; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/FormEvent.php b/vendor/symfony/form/Symfony/Component/Form/FormEvent.php new file mode 100644 index 00000000..57cebade --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/FormEvent.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\EventDispatcher\Event; + +/** + * @author Bernhard Schussek + */ +class FormEvent extends Event +{ + private $form; + protected $data; + + /** + * Constructs an event. + * + * @param FormInterface $form The associated form + * @param mixed $data The data + */ + public function __construct(FormInterface $form, $data) + { + $this->form = $form; + $this->data = $data; + } + + /** + * Returns the form at the source of the event. + * + * @return FormInterface + */ + public function getForm() + { + return $this->form; + } + + /** + * Returns the data associated with this event. + * + * @return mixed + */ + public function getData() + { + return $this->data; + } + + /** + * Allows updating with some filtered data. + * + * @param mixed $data + */ + public function setData($data) + { + $this->data = $data; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/FormEvents.php b/vendor/symfony/form/Symfony/Component/Form/FormEvents.php new file mode 100644 index 00000000..6c4efc5b --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/FormEvents.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * @author Bernhard Schussek + */ +final class FormEvents +{ + const PRE_SUBMIT = 'form.pre_bind'; + + const SUBMIT = 'form.bind'; + + const POST_SUBMIT = 'form.post_bind'; + + const PRE_SET_DATA = 'form.pre_set_data'; + + const POST_SET_DATA = 'form.post_set_data'; + + /** + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link PRE_SUBMIT} instead. + */ + const PRE_BIND = 'form.pre_bind'; + + /** + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link SUBMIT} instead. + */ + const BIND = 'form.bind'; + + /** + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link POST_SUBMIT} instead. + */ + const POST_BIND = 'form.post_bind'; + + private function __construct() + { + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/FormExtensionInterface.php b/vendor/symfony/form/Symfony/Component/Form/FormExtensionInterface.php new file mode 100644 index 00000000..a67055b7 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/FormExtensionInterface.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * Interface for extensions which provide types, type extensions and a guesser. + */ +interface FormExtensionInterface +{ + /** + * Returns a type by name. + * + * @param string $name The name of the type + * + * @return FormTypeInterface The type + * + * @throws Exception\InvalidArgumentException if the given type is not supported by this extension + */ + public function getType($name); + + /** + * Returns whether the given type is supported. + * + * @param string $name The name of the type + * + * @return Boolean Whether the type is supported by this extension + */ + public function hasType($name); + + /** + * Returns the extensions for the given type. + * + * @param string $name The name of the type + * + * @return FormTypeExtensionInterface[] An array of extensions as FormTypeExtensionInterface instances + */ + public function getTypeExtensions($name); + + /** + * Returns whether this extension provides type extensions for the given type. + * + * @param string $name The name of the type + * + * @return Boolean Whether the given type has extensions + */ + public function hasTypeExtensions($name); + + /** + * Returns the type guesser provided by this extension. + * + * @return FormTypeGuesserInterface|null The type guesser + */ + public function getTypeGuesser(); +} diff --git a/vendor/symfony/form/Symfony/Component/Form/FormFactory.php b/vendor/symfony/form/Symfony/Component/Form/FormFactory.php new file mode 100644 index 00000000..a5fd9cc0 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/FormFactory.php @@ -0,0 +1,156 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\Form\Exception\UnexpectedTypeException; + +class FormFactory implements FormFactoryInterface +{ + /** + * @var FormRegistryInterface + */ + private $registry; + + /** + * @var ResolvedFormTypeFactoryInterface + */ + private $resolvedTypeFactory; + + public function __construct(FormRegistryInterface $registry, ResolvedFormTypeFactoryInterface $resolvedTypeFactory) + { + $this->registry = $registry; + $this->resolvedTypeFactory = $resolvedTypeFactory; + } + + /** + * {@inheritdoc} + */ + public function create($type = 'form', $data = null, array $options = array()) + { + return $this->createBuilder($type, $data, $options)->getForm(); + } + + /** + * {@inheritdoc} + */ + public function createNamed($name, $type = 'form', $data = null, array $options = array()) + { + return $this->createNamedBuilder($name, $type, $data, $options)->getForm(); + } + + /** + * {@inheritdoc} + */ + public function createForProperty($class, $property, $data = null, array $options = array()) + { + return $this->createBuilderForProperty($class, $property, $data, $options)->getForm(); + } + + /** + * {@inheritdoc} + */ + public function createBuilder($type = 'form', $data = null, array $options = array()) + { + $name = $type instanceof FormTypeInterface || $type instanceof ResolvedFormTypeInterface + ? $type->getName() + : $type; + + return $this->createNamedBuilder($name, $type, $data, $options); + } + + /** + * {@inheritdoc} + */ + public function createNamedBuilder($name, $type = 'form', $data = null, array $options = array()) + { + if (null !== $data && !array_key_exists('data', $options)) { + $options['data'] = $data; + } + + if ($type instanceof FormTypeInterface) { + $type = $this->resolveType($type); + } elseif (is_string($type)) { + $type = $this->registry->getType($type); + } elseif (!$type instanceof ResolvedFormTypeInterface) { + throw new UnexpectedTypeException($type, 'string, Symfony\Component\Form\ResolvedFormTypeInterface or Symfony\Component\Form\FormTypeInterface'); + } + + return $type->createBuilder($this, $name, $options); + } + + /** + * {@inheritdoc} + */ + public function createBuilderForProperty($class, $property, $data = null, array $options = array()) + { + if (null === $guesser = $this->registry->getTypeGuesser()) { + return $this->createNamedBuilder($property, 'text', $data, $options); + } + + $typeGuess = $guesser->guessType($class, $property); + $maxLengthGuess = $guesser->guessMaxLength($class, $property); + $requiredGuess = $guesser->guessRequired($class, $property); + $patternGuess = $guesser->guessPattern($class, $property); + + $type = $typeGuess ? $typeGuess->getType() : 'text'; + + $maxLength = $maxLengthGuess ? $maxLengthGuess->getValue() : null; + $pattern = $patternGuess ? $patternGuess->getValue() : null; + + if (null !== $pattern) { + $options = array_merge(array('pattern' => $pattern), $options); + } + + if (null !== $maxLength) { + $options = array_merge(array('max_length' => $maxLength), $options); + } + + if ($requiredGuess) { + $options = array_merge(array('required' => $requiredGuess->getValue()), $options); + } + + // user options may override guessed options + if ($typeGuess) { + $options = array_merge($typeGuess->getOptions(), $options); + } + + return $this->createNamedBuilder($property, $type, $data, $options); + } + + /** + * Wraps a type into a ResolvedFormTypeInterface implementation and connects + * it with its parent type. + * + * @param FormTypeInterface $type The type to resolve. + * + * @return ResolvedFormTypeInterface The resolved type. + */ + private function resolveType(FormTypeInterface $type) + { + $parentType = $type->getParent(); + + if ($parentType instanceof FormTypeInterface) { + $parentType = $this->resolveType($parentType); + } elseif (null !== $parentType) { + $parentType = $this->registry->getType($parentType); + } + + return $this->resolvedTypeFactory->createResolvedType( + $type, + // Type extensions are not supported for unregistered type instances, + // i.e. type instances that are passed to the FormFactory directly, + // nor for their parents, if getParent() also returns a type instance. + array(), + $parentType + ); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/FormFactoryBuilder.php b/vendor/symfony/form/Symfony/Component/Form/FormFactoryBuilder.php new file mode 100644 index 00000000..10383e84 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/FormFactoryBuilder.php @@ -0,0 +1,162 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * The default implementation of FormFactoryBuilderInterface. + * + * @author Bernhard Schussek + */ +class FormFactoryBuilder implements FormFactoryBuilderInterface +{ + /** + * @var ResolvedFormTypeFactoryInterface + */ + private $resolvedTypeFactory; + + /** + * @var array + */ + private $extensions = array(); + + /** + * @var array + */ + private $types = array(); + + /** + * @var array + */ + private $typeExtensions = array(); + + /** + * @var array + */ + private $typeGuessers = array(); + + /** + * {@inheritdoc} + */ + public function setResolvedTypeFactory(ResolvedFormTypeFactoryInterface $resolvedTypeFactory) + { + $this->resolvedTypeFactory = $resolvedTypeFactory; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addExtension(FormExtensionInterface $extension) + { + $this->extensions[] = $extension; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addExtensions(array $extensions) + { + $this->extensions = array_merge($this->extensions, $extensions); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addType(FormTypeInterface $type) + { + $this->types[$type->getName()] = $type; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addTypes(array $types) + { + foreach ($types as $type) { + $this->types[$type->getName()] = $type; + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addTypeExtension(FormTypeExtensionInterface $typeExtension) + { + $this->typeExtensions[$typeExtension->getExtendedType()][] = $typeExtension; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addTypeExtensions(array $typeExtensions) + { + foreach ($typeExtensions as $typeExtension) { + $this->typeExtensions[$typeExtension->getExtendedType()][] = $typeExtension; + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addTypeGuesser(FormTypeGuesserInterface $typeGuesser) + { + $this->typeGuessers[] = $typeGuesser; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addTypeGuessers(array $typeGuessers) + { + $this->typeGuessers = array_merge($this->typeGuessers, $typeGuessers); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getFormFactory() + { + $extensions = $this->extensions; + + if (count($this->types) > 0 || count($this->typeExtensions) > 0 || count($this->typeGuessers) > 0) { + if (count($this->typeGuessers) > 1) { + $typeGuesser = new FormTypeGuesserChain($this->typeGuessers); + } else { + $typeGuesser = isset($this->typeGuessers[0]) ? $this->typeGuessers[0] : null; + } + + $extensions[] = new PreloadedExtension($this->types, $this->typeExtensions, $typeGuesser); + } + + $resolvedTypeFactory = $this->resolvedTypeFactory ?: new ResolvedFormTypeFactory(); + $registry = new FormRegistry($extensions, $resolvedTypeFactory); + + return new FormFactory($registry, $resolvedTypeFactory); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/FormFactoryBuilderInterface.php b/vendor/symfony/form/Symfony/Component/Form/FormFactoryBuilderInterface.php new file mode 100644 index 00000000..9370c573 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/FormFactoryBuilderInterface.php @@ -0,0 +1,108 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * A builder for FormFactoryInterface objects. + * + * @author Bernhard Schussek + */ +interface FormFactoryBuilderInterface +{ + /** + * Sets the factory for creating ResolvedFormTypeInterface instances. + * + * @param ResolvedFormTypeFactoryInterface $resolvedTypeFactory + * + * @return FormFactoryBuilderInterface The builder. + */ + public function setResolvedTypeFactory(ResolvedFormTypeFactoryInterface $resolvedTypeFactory); + + /** + * Adds an extension to be loaded by the factory. + * + * @param FormExtensionInterface $extension The extension. + * + * @return FormFactoryBuilderInterface The builder. + */ + public function addExtension(FormExtensionInterface $extension); + + /** + * Adds a list of extensions to be loaded by the factory. + * + * @param array $extensions The extensions. + * + * @return FormFactoryBuilderInterface The builder. + */ + public function addExtensions(array $extensions); + + /** + * Adds a form type to the factory. + * + * @param FormTypeInterface $type The form type. + * + * @return FormFactoryBuilderInterface The builder. + */ + public function addType(FormTypeInterface $type); + + /** + * Adds a list of form types to the factory. + * + * @param array $types The form types. + * + * @return FormFactoryBuilderInterface The builder. + */ + public function addTypes(array $types); + + /** + * Adds a form type extension to the factory. + * + * @param FormTypeExtensionInterface $typeExtension The form type extension. + * + * @return FormFactoryBuilderInterface The builder. + */ + public function addTypeExtension(FormTypeExtensionInterface $typeExtension); + + /** + * Adds a list of form type extensions to the factory. + * + * @param array $typeExtensions The form type extensions. + * + * @return FormFactoryBuilderInterface The builder. + */ + public function addTypeExtensions(array $typeExtensions); + + /** + * Adds a type guesser to the factory. + * + * @param FormTypeGuesserInterface $typeGuesser The type guesser. + * + * @return FormFactoryBuilderInterface The builder. + */ + public function addTypeGuesser(FormTypeGuesserInterface $typeGuesser); + + /** + * Adds a list of type guessers to the factory. + * + * @param array $typeGuessers The type guessers. + * + * @return FormFactoryBuilderInterface The builder. + */ + public function addTypeGuessers(array $typeGuessers); + + /** + * Builds and returns the factory. + * + * @return FormFactoryInterface The form factory. + */ + public function getFormFactory(); +} diff --git a/vendor/symfony/form/Symfony/Component/Form/FormFactoryInterface.php b/vendor/symfony/form/Symfony/Component/Form/FormFactoryInterface.php new file mode 100644 index 00000000..31c46b55 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/FormFactoryInterface.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * @author Bernhard Schussek + */ +interface FormFactoryInterface +{ + /** + * Returns a form. + * + * @see createBuilder() + * + * @param string|FormTypeInterface $type The type of the form + * @param mixed $data The initial data + * @param array $options The options + * + * @return FormInterface The form named after the type + * + * @throws \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException if any given option is not applicable to the given type + */ + public function create($type = 'form', $data = null, array $options = array()); + + /** + * Returns a form. + * + * @see createNamedBuilder() + * + * @param string|integer $name The name of the form + * @param string|FormTypeInterface $type The type of the form + * @param mixed $data The initial data + * @param array $options The options + * + * @return FormInterface The form + * + * @throws \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException if any given option is not applicable to the given type + */ + public function createNamed($name, $type = 'form', $data = null, array $options = array()); + + /** + * Returns a form for a property of a class. + * + * @see createBuilderForProperty() + * + * @param string $class The fully qualified class name + * @param string $property The name of the property to guess for + * @param mixed $data The initial data + * @param array $options The options for the builder + * + * @return FormInterface The form named after the property + * + * @throws \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException if any given option is not applicable to the form type + */ + public function createForProperty($class, $property, $data = null, array $options = array()); + + /** + * Returns a form builder. + * + * @param string|FormTypeInterface $type The type of the form + * @param mixed $data The initial data + * @param array $options The options + * + * @return FormBuilderInterface The form builder + * + * @throws \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException if any given option is not applicable to the given type + */ + public function createBuilder($type = 'form', $data = null, array $options = array()); + + /** + * Returns a form builder. + * + * @param string|integer $name The name of the form + * @param string|FormTypeInterface $type The type of the form + * @param mixed $data The initial data + * @param array $options The options + * + * @return FormBuilderInterface The form builder + * + * @throws \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException if any given option is not applicable to the given type + */ + public function createNamedBuilder($name, $type = 'form', $data = null, array $options = array()); + + /** + * Returns a form builder for a property of a class. + * + * If any of the 'max_length', 'required' and type options can be guessed, + * and are not provided in the options argument, the guessed value is used. + * + * @param string $class The fully qualified class name + * @param string $property The name of the property to guess for + * @param mixed $data The initial data + * @param array $options The options for the builder + * + * @return FormBuilderInterface The form builder named after the property + * + * @throws \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException if any given option is not applicable to the form type + */ + public function createBuilderForProperty($class, $property, $data = null, array $options = array()); +} diff --git a/vendor/symfony/form/Symfony/Component/Form/FormInterface.php b/vendor/symfony/form/Symfony/Component/Form/FormInterface.php new file mode 100644 index 00000000..5a852e86 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/FormInterface.php @@ -0,0 +1,288 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * A form group bundling multiple forms in a hierarchical structure. + * + * @author Bernhard Schussek + */ +interface FormInterface extends \ArrayAccess, \Traversable, \Countable +{ + /** + * Sets the parent form. + * + * @param FormInterface|null $parent The parent form or null if it's the root. + * + * @return FormInterface The form instance + * + * @throws Exception\AlreadySubmittedException If the form has already been submitted. + * @throws Exception\LogicException When trying to set a parent for a form with + * an empty name. + */ + public function setParent(FormInterface $parent = null); + + /** + * Returns the parent form. + * + * @return FormInterface|null The parent form or null if there is none. + */ + public function getParent(); + + /** + * Adds a child to the form. + * + * @param FormInterface|string|integer $child The FormInterface instance or the name of the child. + * @param string|null $type The child's type, if a name was passed. + * @param array $options The child's options, if a name was passed. + * + * @return FormInterface The form instance + * + * @throws Exception\AlreadySubmittedException If the form has already been submitted. + * @throws Exception\LogicException When trying to add a child to a non-compound form. + * @throws Exception\UnexpectedTypeException If $child or $type has an unexpected type. + */ + public function add($child, $type = null, array $options = array()); + + /** + * Returns the child with the given name. + * + * @param string $name The name of the child + * + * @return FormInterface The child form + * + * @throws \OutOfBoundsException If the named child does not exist. + */ + public function get($name); + + /** + * Returns whether a child with the given name exists. + * + * @param string $name The name of the child + * + * @return Boolean + */ + public function has($name); + + /** + * Removes a child from the form. + * + * @param string $name The name of the child to remove + * + * @return FormInterface The form instance + * + * @throws Exception\AlreadySubmittedException If the form has already been submitted. + */ + public function remove($name); + + /** + * Returns all children in this group. + * + * @return FormInterface[] An array of FormInterface instances + */ + public function all(); + + /** + * Returns all errors. + * + * @return FormError[] An array of FormError instances that occurred during validation + */ + public function getErrors(); + + /** + * Updates the form with default data. + * + * @param mixed $modelData The data formatted as expected for the underlying object + * + * @return FormInterface The form instance + * + * @throws Exception\AlreadySubmittedException If the form has already been submitted. + * @throws Exception\LogicException If listeners try to call setData in a cycle. Or if + * the view data does not match the expected type + * according to {@link FormConfigInterface::getDataClass}. + */ + public function setData($modelData); + + /** + * Returns the data in the format needed for the underlying object. + * + * @return mixed + */ + public function getData(); + + /** + * Returns the normalized data of the field. + * + * @return mixed When the field is not submitted, the default data is returned. + * When the field is submitted, the normalized submitted data is + * returned if the field is valid, null otherwise. + */ + public function getNormData(); + + /** + * Returns the data transformed by the value transformer. + * + * @return mixed + */ + public function getViewData(); + + /** + * Returns the extra data. + * + * @return array The submitted data which do not belong to a child + */ + public function getExtraData(); + + /** + * Returns the form's configuration. + * + * @return FormConfigInterface The configuration. + */ + public function getConfig(); + + /** + * Returns whether the form is submitted. + * + * @return Boolean true if the form is submitted, false otherwise + */ + public function isSubmitted(); + + /** + * Returns the name by which the form is identified in forms. + * + * @return string The name of the form. + */ + public function getName(); + + /** + * Returns the property path that the form is mapped to. + * + * @return \Symfony\Component\PropertyAccess\PropertyPathInterface The property path. + */ + public function getPropertyPath(); + + /** + * Adds an error to this form. + * + * @param FormError $error + * + * @return FormInterface The form instance + */ + public function addError(FormError $error); + + /** + * Returns whether the form and all children are valid. + * + * If the form is not submitted, this method always returns false. + * + * @return Boolean + */ + public function isValid(); + + /** + * Returns whether the form is required to be filled out. + * + * If the form has a parent and the parent is not required, this method + * will always return false. Otherwise the value set with setRequired() + * is returned. + * + * @return Boolean + */ + public function isRequired(); + + /** + * Returns whether this form is disabled. + * + * The content of a disabled form is displayed, but not allowed to be + * modified. The validation of modified disabled forms should fail. + * + * Forms whose parents are disabled are considered disabled regardless of + * their own state. + * + * @return Boolean + */ + public function isDisabled(); + + /** + * Returns whether the form is empty. + * + * @return Boolean + */ + public function isEmpty(); + + /** + * Returns whether the data in the different formats is synchronized. + * + * @return Boolean + */ + public function isSynchronized(); + + /** + * Initializes the form tree. + * + * Should be called on the root form after constructing the tree. + * + * @return FormInterface The form instance. + */ + public function initialize(); + + /** + * Inspects the given request and calls {@link submit()} if the form was + * submitted. + * + * Internally, the request is forwarded to the configured + * {@link RequestHandlerInterface} instance, which determines whether to + * submit the form or not. + * + * @param mixed $request The request to handle. + * + * @return FormInterface The form instance. + */ + public function handleRequest($request = null); + + /** + * Submits data to the form, transforms and validates it. + * + * @param null|string|array $submittedData The submitted data. + * @param Boolean $clearMissing Whether to set fields to NULL + * when they are missing in the + * submitted data. + * + * @return FormInterface The form instance + * + * @throws Exception\AlreadySubmittedException If the form has already been submitted. + */ + public function submit($submittedData, $clearMissing = true); + + /** + * Returns the root of the form tree. + * + * @return FormInterface The root of the tree + */ + public function getRoot(); + + /** + * Returns whether the field is the root of the form tree. + * + * @return Boolean + */ + public function isRoot(); + + /** + * Creates a view. + * + * @param FormView $parent The parent view + * + * @return FormView The view + */ + public function createView(FormView $parent = null); +} diff --git a/vendor/symfony/form/Symfony/Component/Form/FormRegistry.php b/vendor/symfony/form/Symfony/Component/Form/FormRegistry.php new file mode 100644 index 00000000..0267a565 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/FormRegistry.php @@ -0,0 +1,180 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\Form\Exception\ExceptionInterface; +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\Form\Exception\InvalidArgumentException; + +/** + * The central registry of the Form component. + * + * @author Bernhard Schussek + */ +class FormRegistry implements FormRegistryInterface +{ + /** + * Extensions + * + * @var FormExtensionInterface[] An array of FormExtensionInterface + */ + private $extensions = array(); + + /** + * @var array + */ + private $types = array(); + + /** + * @var FormTypeGuesserInterface|false|null + */ + private $guesser = false; + + /** + * @var ResolvedFormTypeFactoryInterface + */ + private $resolvedTypeFactory; + + /** + * Constructor. + * + * @param FormExtensionInterface[] $extensions An array of FormExtensionInterface + * @param ResolvedFormTypeFactoryInterface $resolvedTypeFactory The factory for resolved form types. + * + * @throws UnexpectedTypeException if any extension does not implement FormExtensionInterface + */ + public function __construct(array $extensions, ResolvedFormTypeFactoryInterface $resolvedTypeFactory) + { + foreach ($extensions as $extension) { + if (!$extension instanceof FormExtensionInterface) { + throw new UnexpectedTypeException($extension, 'Symfony\Component\Form\FormExtensionInterface'); + } + } + + $this->extensions = $extensions; + $this->resolvedTypeFactory = $resolvedTypeFactory; + } + + /** + * {@inheritdoc} + */ + public function getType($name) + { + if (!is_string($name)) { + throw new UnexpectedTypeException($name, 'string'); + } + + if (!isset($this->types[$name])) { + /** @var FormTypeInterface $type */ + $type = null; + + foreach ($this->extensions as $extension) { + /* @var FormExtensionInterface $extension */ + if ($extension->hasType($name)) { + $type = $extension->getType($name); + break; + } + } + + if (!$type) { + throw new InvalidArgumentException(sprintf('Could not load type "%s"', $name)); + } + + $this->resolveAndAddType($type); + } + + return $this->types[$name]; + } + + /** + * Wraps a type into a ResolvedFormTypeInterface implementation and connects + * it with its parent type. + * + * @param FormTypeInterface $type The type to resolve. + * + * @return ResolvedFormTypeInterface The resolved type. + */ + private function resolveAndAddType(FormTypeInterface $type) + { + $parentType = $type->getParent(); + + if ($parentType instanceof FormTypeInterface) { + $this->resolveAndAddType($parentType); + $parentType = $parentType->getName(); + } + + $typeExtensions = array(); + + foreach ($this->extensions as $extension) { + /* @var FormExtensionInterface $extension */ + $typeExtensions = array_merge( + $typeExtensions, + $extension->getTypeExtensions($type->getName()) + ); + } + + $this->types[$type->getName()] = $this->resolvedTypeFactory->createResolvedType( + $type, + $typeExtensions, + $parentType ? $this->getType($parentType) : null + ); + } + + /** + * {@inheritdoc} + */ + public function hasType($name) + { + if (isset($this->types[$name])) { + return true; + } + + try { + $this->getType($name); + } catch (ExceptionInterface $e) { + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function getTypeGuesser() + { + if (false === $this->guesser) { + $guessers = array(); + + foreach ($this->extensions as $extension) { + /* @var FormExtensionInterface $extension */ + $guesser = $extension->getTypeGuesser(); + + if ($guesser) { + $guessers[] = $guesser; + } + } + + $this->guesser = !empty($guessers) ? new FormTypeGuesserChain($guessers) : null; + } + + return $this->guesser; + } + + /** + * {@inheritdoc} + */ + public function getExtensions() + { + return $this->extensions; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/FormRegistryInterface.php b/vendor/symfony/form/Symfony/Component/Form/FormRegistryInterface.php new file mode 100644 index 00000000..16cd9384 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/FormRegistryInterface.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * The central registry of the Form component. + * + * @author Bernhard Schussek + */ +interface FormRegistryInterface +{ + /** + * Returns a form type by name. + * + * This methods registers the type extensions from the form extensions. + * + * @param string $name The name of the type + * + * @return ResolvedFormTypeInterface The type + * + * @throws Exception\UnexpectedTypeException if the passed name is not a string + * @throws Exception\InvalidArgumentException if the type can not be retrieved from any extension + */ + public function getType($name); + + /** + * Returns whether the given form type is supported. + * + * @param string $name The name of the type + * + * @return Boolean Whether the type is supported + */ + public function hasType($name); + + /** + * Returns the guesser responsible for guessing types. + * + * @return FormTypeGuesserInterface|null + */ + public function getTypeGuesser(); + + /** + * Returns the extensions loaded by the framework. + * + * @return array + */ + public function getExtensions(); +} diff --git a/vendor/symfony/form/Symfony/Component/Form/FormRenderer.php b/vendor/symfony/form/Symfony/Component/Form/FormRenderer.php new file mode 100644 index 00000000..09b01056 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/FormRenderer.php @@ -0,0 +1,304 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\Form\Exception\LogicException; +use Symfony\Component\Form\Exception\BadMethodCallException; +use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; + +/** + * Renders a form into HTML using a rendering engine. + * + * @author Bernhard Schussek + */ +class FormRenderer implements FormRendererInterface +{ + const CACHE_KEY_VAR = 'unique_block_prefix'; + + /** + * @var FormRendererEngineInterface + */ + private $engine; + + /** + * @var CsrfProviderInterface + */ + private $csrfProvider; + + /** + * @var array + */ + private $blockNameHierarchyMap = array(); + + /** + * @var array + */ + private $hierarchyLevelMap = array(); + + /** + * @var array + */ + private $variableStack = array(); + + public function __construct(FormRendererEngineInterface $engine, CsrfProviderInterface $csrfProvider = null) + { + $this->engine = $engine; + $this->csrfProvider = $csrfProvider; + } + + /** + * {@inheritdoc} + */ + public function getEngine() + { + return $this->engine; + } + + /** + * {@inheritdoc} + */ + public function setTheme(FormView $view, $themes) + { + $this->engine->setTheme($view, $themes); + } + + /** + * {@inheritdoc} + */ + public function renderCsrfToken($intention) + { + if (null === $this->csrfProvider) { + throw new BadMethodCallException('CSRF token can only be generated if a CsrfProviderInterface is injected in the constructor.'); + } + + return $this->csrfProvider->generateCsrfToken($intention); + } + + /** + * {@inheritdoc} + */ + public function renderBlock(FormView $view, $blockName, array $variables = array()) + { + $resource = $this->engine->getResourceForBlockName($view, $blockName); + + if (!$resource) { + throw new LogicException(sprintf('No block "%s" found while rendering the form.', $blockName)); + } + + $viewCacheKey = $view->vars[self::CACHE_KEY_VAR]; + + // The variables are cached globally for a view (instead of for the + // current suffix) + if (!isset($this->variableStack[$viewCacheKey])) { + $this->variableStack[$viewCacheKey] = array(); + + // The default variable scope contains all view variables, merged with + // the variables passed explicitly to the helper + $scopeVariables = $view->vars; + + $varInit = true; + } else { + // Reuse the current scope and merge it with the explicitly passed variables + $scopeVariables = end($this->variableStack[$viewCacheKey]); + + $varInit = false; + } + + // Merge the passed with the existing attributes + if (isset($variables['attr']) && isset($scopeVariables['attr'])) { + $variables['attr'] = array_replace($scopeVariables['attr'], $variables['attr']); + } + + // Merge the passed with the exist *label* attributes + if (isset($variables['label_attr']) && isset($scopeVariables['label_attr'])) { + $variables['label_attr'] = array_replace($scopeVariables['label_attr'], $variables['label_attr']); + } + + // Do not use array_replace_recursive(), otherwise array variables + // cannot be overwritten + $variables = array_replace($scopeVariables, $variables); + + $this->variableStack[$viewCacheKey][] = $variables; + + // Do the rendering + $html = $this->engine->renderBlock($view, $resource, $blockName, $variables); + + // Clear the stack + array_pop($this->variableStack[$viewCacheKey]); + + if ($varInit) { + unset($this->variableStack[$viewCacheKey]); + } + + return $html; + } + + /** + * {@inheritdoc} + */ + public function searchAndRenderBlock(FormView $view, $blockNameSuffix, array $variables = array()) + { + $renderOnlyOnce = 'row' === $blockNameSuffix || 'widget' === $blockNameSuffix; + + if ($renderOnlyOnce && $view->isRendered()) { + return ''; + } + + // The cache key for storing the variables and types + $viewCacheKey = $view->vars[self::CACHE_KEY_VAR]; + $viewAndSuffixCacheKey = $viewCacheKey.$blockNameSuffix; + + // In templates, we have to deal with two kinds of block hierarchies: + // + // +---------+ +---------+ + // | Theme B | -------> | Theme A | + // +---------+ +---------+ + // + // form_widget -------> form_widget + // ^ + // | + // choice_widget -----> choice_widget + // + // The first kind of hierarchy is the theme hierarchy. This allows to + // override the block "choice_widget" from Theme A in the extending + // Theme B. This kind of inheritance needs to be supported by the + // template engine and, for example, offers "parent()" or similar + // functions to fall back from the custom to the parent implementation. + // + // The second kind of hierarchy is the form type hierarchy. This allows + // to implement a custom "choice_widget" block (no matter in which theme), + // or to fallback to the block of the parent type, which would be + // "form_widget" in this example (again, no matter in which theme). + // If the designer wants to explicitly fallback to "form_widget" in his + // custom "choice_widget", for example because he only wants to wrap + // a
around the original implementation, he can simply call the + // widget() function again to render the block for the parent type. + // + // The second kind is implemented in the following blocks. + if (!isset($this->blockNameHierarchyMap[$viewAndSuffixCacheKey])) { + // INITIAL CALL + // Calculate the hierarchy of template blocks and start on + // the bottom level of the hierarchy (= "__
" block) + $blockNameHierarchy = array(); + foreach ($view->vars['block_prefixes'] as $blockNamePrefix) { + $blockNameHierarchy[] = $blockNamePrefix.'_'.$blockNameSuffix; + } + $hierarchyLevel = count($blockNameHierarchy) - 1; + + $hierarchyInit = true; + } else { + // RECURSIVE CALL + // If a block recursively calls searchAndRenderBlock() again, resume rendering + // using the parent type in the hierarchy. + $blockNameHierarchy = $this->blockNameHierarchyMap[$viewAndSuffixCacheKey]; + $hierarchyLevel = $this->hierarchyLevelMap[$viewAndSuffixCacheKey] - 1; + + $hierarchyInit = false; + } + + // The variables are cached globally for a view (instead of for the + // current suffix) + if (!isset($this->variableStack[$viewCacheKey])) { + $this->variableStack[$viewCacheKey] = array(); + + // The default variable scope contains all view variables, merged with + // the variables passed explicitly to the helper + $scopeVariables = $view->vars; + + $varInit = true; + } else { + // Reuse the current scope and merge it with the explicitly passed variables + $scopeVariables = end($this->variableStack[$viewCacheKey]); + + $varInit = false; + } + + // Load the resource where this block can be found + $resource = $this->engine->getResourceForBlockNameHierarchy($view, $blockNameHierarchy, $hierarchyLevel); + + // Update the current hierarchy level to the one at which the resource was + // found. For example, if looking for "choice_widget", but only a resource + // is found for its parent "form_widget", then the level is updated here + // to the parent level. + $hierarchyLevel = $this->engine->getResourceHierarchyLevel($view, $blockNameHierarchy, $hierarchyLevel); + + // The actually existing block name in $resource + $blockName = $blockNameHierarchy[$hierarchyLevel]; + + // Escape if no resource exists for this block + if (!$resource) { + throw new LogicException(sprintf( + 'Unable to render the form as none of the following blocks exist: "%s".', + implode('", "', array_reverse($blockNameHierarchy)) + )); + } + + // Merge the passed with the existing attributes + if (isset($variables['attr']) && isset($scopeVariables['attr'])) { + $variables['attr'] = array_replace($scopeVariables['attr'], $variables['attr']); + } + + // Merge the passed with the exist *label* attributes + if (isset($variables['label_attr']) && isset($scopeVariables['label_attr'])) { + $variables['label_attr'] = array_replace($scopeVariables['label_attr'], $variables['label_attr']); + } + + // Do not use array_replace_recursive(), otherwise array variables + // cannot be overwritten + $variables = array_replace($scopeVariables, $variables); + + // In order to make recursive calls possible, we need to store the block hierarchy, + // the current level of the hierarchy and the variables so that this method can + // resume rendering one level higher of the hierarchy when it is called recursively. + // + // We need to store these values in maps (associative arrays) because within a + // call to widget() another call to widget() can be made, but for a different view + // object. These nested calls should not override each other. + $this->blockNameHierarchyMap[$viewAndSuffixCacheKey] = $blockNameHierarchy; + $this->hierarchyLevelMap[$viewAndSuffixCacheKey] = $hierarchyLevel; + + // We also need to store the variables for the view so that we can render other + // blocks for the same view using the same variables as in the outer block. + $this->variableStack[$viewCacheKey][] = $variables; + + // Do the rendering + $html = $this->engine->renderBlock($view, $resource, $blockName, $variables); + + // Clear the stack + array_pop($this->variableStack[$viewCacheKey]); + + // Clear the caches if they were filled for the first time within + // this function call + if ($hierarchyInit) { + unset($this->blockNameHierarchyMap[$viewAndSuffixCacheKey]); + unset($this->hierarchyLevelMap[$viewAndSuffixCacheKey]); + } + + if ($varInit) { + unset($this->variableStack[$viewCacheKey]); + } + + if ($renderOnlyOnce) { + $view->setRendered(); + } + + return $html; + } + + /** + * {@inheritdoc} + */ + public function humanize($text) + { + return ucfirst(trim(strtolower(preg_replace(array('/([A-Z])/', '/[_\s]+/'), array('_$1', ' '), $text)))); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/FormRendererEngineInterface.php b/vendor/symfony/form/Symfony/Component/Form/FormRendererEngineInterface.php new file mode 100644 index 00000000..e06824ec --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/FormRendererEngineInterface.php @@ -0,0 +1,150 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * Adapter for rendering form templates with a specific templating engine. + * + * @author Bernhard Schussek + */ +interface FormRendererEngineInterface +{ + /** + * Sets the theme(s) to be used for rendering a view and its children. + * + * @param FormView $view The view to assign the theme(s) to. + * @param mixed $themes The theme(s). The type of these themes + * is open to the implementation. + */ + public function setTheme(FormView $view, $themes); + + /** + * Returns the resource for a block name. + * + * The resource is first searched in the themes attached to $view, then + * in the themes of its parent view and so on, until a resource was found. + * + * The type of the resource is decided by the implementation. The resource + * is later passed to {@link renderBlock()} by the rendering algorithm. + * + * @param FormView $view The view for determining the used themes. + * First the themes attached directly to the + * view with {@link setTheme()} are considered, + * then the ones of its parent etc. + * @param string $blockName The name of the block to render. + * + * @return mixed The renderer resource or false, if none was found. + */ + public function getResourceForBlockName(FormView $view, $blockName); + + /** + * Returns the resource for a block hierarchy. + * + * A block hierarchy is an array which starts with the root of the hierarchy + * and continues with the child of that root, the child of that child etc. + * The following is an example for a block hierarchy: + * + * + * form_widget + * text_widget + * url_widget + * + * + * In this example, "url_widget" is the most specific block, while the other + * blocks are its ancestors in the hierarchy. + * + * The second parameter $hierarchyLevel determines the level of the hierarchy + * that should be rendered. For example, if $hierarchyLevel is 2 for the + * above hierarchy, the engine will first look for the block "url_widget", + * then, if that does not exist, for the block "text_widget" etc. + * + * The type of the resource is decided by the implementation. The resource + * is later passed to {@link renderBlock()} by the rendering algorithm. + * + * @param FormView $view The view for determining the + * used themes. First the themes + * attached directly to the view + * with {@link setTheme()} are + * considered, then the ones of + * its parent etc. + * @param array $blockNameHierarchy The block name hierarchy, with + * the root block at the beginning. + * @param integer $hierarchyLevel The level in the hierarchy at + * which to start looking. Level 0 + * indicates the root block, i.e. + * the first element of + * $blockNameHierarchy. + * + * @return mixed The renderer resource or false, if none was found. + */ + public function getResourceForBlockNameHierarchy(FormView $view, array $blockNameHierarchy, $hierarchyLevel); + + /** + * Returns the hierarchy level at which a resource can be found. + * + * A block hierarchy is an array which starts with the root of the hierarchy + * and continues with the child of that root, the child of that child etc. + * The following is an example for a block hierarchy: + * + * + * form_widget + * text_widget + * url_widget + * + * + * The second parameter $hierarchyLevel determines the level of the hierarchy + * that should be rendered. + * + * If we call this method with the hierarchy level 2, the engine will first + * look for a resource for block "url_widget". If such a resource exists, + * the method returns 2. Otherwise it tries to find a resource for block + * "text_widget" (at level 1) and, again, returns 1 if a resource was found. + * The method continues to look for resources until the root level was + * reached and nothing was found. In this case false is returned. + * + * The type of the resource is decided by the implementation. The resource + * is later passed to {@link renderBlock()} by the rendering algorithm. + * + * @param FormView $view The view for determining the + * used themes. First the themes + * attached directly to the view + * with {@link setTheme()} are + * considered, then the ones of + * its parent etc. + * @param array $blockNameHierarchy The block name hierarchy, with + * the root block at the beginning. + * @param integer $hierarchyLevel The level in the hierarchy at + * which to start looking. Level 0 + * indicates the root block, i.e. + * the first element of + * $blockNameHierarchy. + * + * @return integer|Boolean The hierarchy level or false, if no resource was found. + */ + public function getResourceHierarchyLevel(FormView $view, array $blockNameHierarchy, $hierarchyLevel); + + /** + * Renders a block in the given renderer resource. + * + * The resource can be obtained by calling {@link getResourceForBlock()} + * or {@link getResourceForBlockHierarchy()}. The type of the resource is + * decided by the implementation. + * + * @param FormView $view The view to render. + * @param mixed $resource The renderer resource. + * @param string $blockName The name of the block to render. + * @param array $variables The variables to pass to the template. + * + * @return string The HTML markup. + */ + public function renderBlock(FormView $view, $resource, $blockName, array $variables = array()); +} diff --git a/vendor/symfony/form/Symfony/Component/Form/FormRendererInterface.php b/vendor/symfony/form/Symfony/Component/Form/FormRendererInterface.php new file mode 100644 index 00000000..848e7125 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/FormRendererInterface.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * Renders a form into HTML. + * + * @author Bernhard Schussek + */ +interface FormRendererInterface +{ + /** + * Returns the engine used by this renderer. + * + * @return FormRendererEngineInterface The renderer engine. + */ + public function getEngine(); + + /** + * Sets the theme(s) to be used for rendering a view and its children. + * + * @param FormView $view The view to assign the theme(s) to. + * @param mixed $themes The theme(s). The type of these themes + * is open to the implementation. + */ + public function setTheme(FormView $view, $themes); + + /** + * Renders a named block of the form theme. + * + * @param FormView $view The view for which to render the block. + * @param string $blockName The name of the block. + * @param array $variables The variables to pass to the template. + * + * @return string The HTML markup + */ + public function renderBlock(FormView $view, $blockName, array $variables = array()); + + /** + * Searches and renders a block for a given name suffix. + * + * The block is searched by combining the block names stored in the + * form view with the given suffix. If a block name is found, that + * block is rendered. + * + * If this method is called recursively, the block search is continued + * where a block was found before. + * + * @param FormView $view The view for which to render the block. + * @param string $blockNameSuffix The suffix of the block name. + * @param array $variables The variables to pass to the template. + * + * @return string The HTML markup + */ + public function searchAndRenderBlock(FormView $view, $blockNameSuffix, array $variables = array()); + + /** + * Renders a CSRF token. + * + * Use this helper for CSRF protection without the overhead of creating a + * form. + * + * + * + * + * + * Check the token in your action using the same intention. + * + * + * $csrfProvider = $this->get('form.csrf_provider'); + * if (!$csrfProvider->isCsrfTokenValid('rm_user_'.$user->getId(), $token)) { + * throw new \RuntimeException('CSRF attack detected.'); + * } + * + * + * @param string $intention The intention of the protected action + * + * @return string A CSRF token + */ + public function renderCsrfToken($intention); + + /** + * Makes a technical name human readable. + * + * Sequences of underscores are replaced by single spaces. The first letter + * of the resulting string is capitalized, while all other letters are + * turned to lowercase. + * + * @param string $text The text to humanize. + * + * @return string The humanized text. + */ + public function humanize($text); +} diff --git a/vendor/symfony/form/Symfony/Component/Form/FormTypeExtensionInterface.php b/vendor/symfony/form/Symfony/Component/Form/FormTypeExtensionInterface.php new file mode 100644 index 00000000..9866b28b --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/FormTypeExtensionInterface.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +/** + * @author Bernhard Schussek + */ +interface FormTypeExtensionInterface +{ + /** + * Builds the form. + * + * This method is called after the extended type has built the form to + * further modify it. + * + * @see FormTypeInterface::buildForm() + * + * @param FormBuilderInterface $builder The form builder + * @param array $options The options + */ + public function buildForm(FormBuilderInterface $builder, array $options); + + /** + * Builds the view. + * + * This method is called after the extended type has built the view to + * further modify it. + * + * @see FormTypeInterface::buildView() + * + * @param FormView $view The view + * @param FormInterface $form The form + * @param array $options The options + */ + public function buildView(FormView $view, FormInterface $form, array $options); + + /** + * Finishes the view. + * + * This method is called after the extended type has finished the view to + * further modify it. + * + * @see FormTypeInterface::finishView() + * + * @param FormView $view The view + * @param FormInterface $form The form + * @param array $options The options + */ + public function finishView(FormView $view, FormInterface $form, array $options); + + /** + * Overrides the default options from the extended type. + * + * @param OptionsResolverInterface $resolver The resolver for the options. + */ + public function setDefaultOptions(OptionsResolverInterface $resolver); + + /** + * Returns the name of the type being extended. + * + * @return string The name of the type being extended + */ + public function getExtendedType(); +} diff --git a/vendor/symfony/form/Symfony/Component/Form/FormTypeGuesserChain.php b/vendor/symfony/form/Symfony/Component/Form/FormTypeGuesserChain.php new file mode 100644 index 00000000..c7f8ece3 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/FormTypeGuesserChain.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\Form\Guess\Guess; +use Symfony\Component\Form\Exception\UnexpectedTypeException; + +class FormTypeGuesserChain implements FormTypeGuesserInterface +{ + private $guessers = array(); + + /** + * Constructor. + * + * @param array $guessers Guessers as instances of FormTypeGuesserInterface + * + * @throws UnexpectedTypeException if any guesser does not implement FormTypeGuesserInterface + */ + public function __construct(array $guessers) + { + foreach ($guessers as $guesser) { + if (!$guesser instanceof FormTypeGuesserInterface) { + throw new UnexpectedTypeException($guesser, 'Symfony\Component\Form\FormTypeGuesserInterface'); + } + + if ($guesser instanceof self) { + $this->guessers = array_merge($this->guessers, $guesser->guessers); + } else { + $this->guessers[] = $guesser; + } + } + } + + /** + * {@inheritDoc} + */ + public function guessType($class, $property) + { + return $this->guess(function ($guesser) use ($class, $property) { + return $guesser->guessType($class, $property); + }); + } + + /** + * {@inheritDoc} + */ + public function guessRequired($class, $property) + { + return $this->guess(function ($guesser) use ($class, $property) { + return $guesser->guessRequired($class, $property); + }); + } + + /** + * {@inheritDoc} + */ + public function guessMaxLength($class, $property) + { + return $this->guess(function ($guesser) use ($class, $property) { + return $guesser->guessMaxLength($class, $property); + }); + } + + /** + * {@inheritDoc} + */ + public function guessPattern($class, $property) + { + return $this->guess(function ($guesser) use ($class, $property) { + return $guesser->guessPattern($class, $property); + }); + } + + /** + * Executes a closure for each guesser and returns the best guess from the + * return values + * + * @param \Closure $closure The closure to execute. Accepts a guesser + * as argument and should return a Guess instance + * + * @return Guess The guess with the highest confidence + */ + private function guess(\Closure $closure) + { + $guesses = array(); + + foreach ($this->guessers as $guesser) { + if ($guess = $closure($guesser)) { + $guesses[] = $guess; + } + } + + return Guess::getBestGuess($guesses); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/FormTypeGuesserInterface.php b/vendor/symfony/form/Symfony/Component/Form/FormTypeGuesserInterface.php new file mode 100644 index 00000000..e8b603fa --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/FormTypeGuesserInterface.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * @author Bernhard Schussek + */ +interface FormTypeGuesserInterface +{ + /** + * Returns a field guess for a property name of a class + * + * @param string $class The fully qualified class name + * @param string $property The name of the property to guess for + * + * @return Guess\TypeGuess A guess for the field's type and options + */ + public function guessType($class, $property); + + /** + * Returns a guess whether a property of a class is required + * + * @param string $class The fully qualified class name + * @param string $property The name of the property to guess for + * + * @return Guess\Guess A guess for the field's required setting + */ + public function guessRequired($class, $property); + + /** + * Returns a guess about the field's maximum length + * + * @param string $class The fully qualified class name + * @param string $property The name of the property to guess for + * + * @return Guess\Guess A guess for the field's maximum length + */ + public function guessMaxLength($class, $property); + + /** + * Returns a guess about the field's pattern + * + * - When you have a min value, you guess a min length of this min (LOW_CONFIDENCE) , lines below + * - If this value is a float type, this is wrong so you guess null with MEDIUM_CONFIDENCE to override the previous guess. + * Example: + * You want a float greater than 5, 4.512313 is not valid but length(4.512314) > length(5) + * @link https://github.com/symfony/symfony/pull/3927 + * + * @param string $class The fully qualified class name + * @param string $property The name of the property to guess for + * + * @return Guess\Guess A guess for the field's required pattern + */ + public function guessPattern($class, $property); +} diff --git a/vendor/symfony/form/Symfony/Component/Form/FormTypeInterface.php b/vendor/symfony/form/Symfony/Component/Form/FormTypeInterface.php new file mode 100644 index 00000000..bcef73c6 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/FormTypeInterface.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +/** + * @author Bernhard Schussek + */ +interface FormTypeInterface +{ + /** + * Builds the form. + * + * This method is called for each type in the hierarchy starting form the + * top most type. Type extensions can further modify the form. + * + * @see FormTypeExtensionInterface::buildForm() + * + * @param FormBuilderInterface $builder The form builder + * @param array $options The options + */ + public function buildForm(FormBuilderInterface $builder, array $options); + + /** + * Builds the form view. + * + * This method is called for each type in the hierarchy starting form the + * top most type. Type extensions can further modify the view. + * + * A view of a form is built before the views of the child forms are built. + * This means that you cannot access child views in this method. If you need + * to do so, move your logic to {@link finishView()} instead. + * + * @see FormTypeExtensionInterface::buildView() + * + * @param FormView $view The view + * @param FormInterface $form The form + * @param array $options The options + */ + public function buildView(FormView $view, FormInterface $form, array $options); + + /** + * Finishes the form view. + * + * This method gets called for each type in the hierarchy starting form the + * top most type. Type extensions can further modify the view. + * + * When this method is called, views of the form's children have already + * been built and finished and can be accessed. You should only implement + * such logic in this method that actually accesses child views. For everything + * else you are recommended to implement {@link buildView()} instead. + * + * @see FormTypeExtensionInterface::finishView() + * + * @param FormView $view The view + * @param FormInterface $form The form + * @param array $options The options + */ + public function finishView(FormView $view, FormInterface $form, array $options); + + /** + * Sets the default options for this type. + * + * @param OptionsResolverInterface $resolver The resolver for the options. + */ + public function setDefaultOptions(OptionsResolverInterface $resolver); + + /** + * Returns the name of the parent type. + * + * You can also return a type instance from this method, although doing so + * is discouraged because it leads to a performance penalty. The support + * for returning type instances may be dropped from future releases. + * + * @return string|null|FormTypeInterface The name of the parent type if any, null otherwise. + */ + public function getParent(); + + /** + * Returns the name of this type. + * + * @return string The name of this type + */ + public function getName(); +} diff --git a/vendor/symfony/form/Symfony/Component/Form/FormView.php b/vendor/symfony/form/Symfony/Component/Form/FormView.php new file mode 100644 index 00000000..1f53ec6a --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/FormView.php @@ -0,0 +1,159 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\Form\Exception\BadMethodCallException; + +/** + * @author Bernhard Schussek + */ +class FormView implements \ArrayAccess, \IteratorAggregate, \Countable +{ + /** + * The variables assigned to this view. + * @var array + */ + public $vars = array( + 'value' => null, + 'attr' => array(), + ); + + /** + * The parent view. + * @var FormView + */ + public $parent; + + /** + * The child views. + * @var array + */ + public $children = array(); + + /** + * Is the form attached to this renderer rendered? + * + * Rendering happens when either the widget or the row method was called. + * Row implicitly includes widget, however certain rendering mechanisms + * have to skip widget rendering when a row is rendered. + * + * @var Boolean + */ + private $rendered = false; + + public function __construct(FormView $parent = null) + { + $this->parent = $parent; + } + + /** + * Returns whether the view was already rendered. + * + * @return Boolean Whether this view's widget is rendered. + */ + public function isRendered() + { + $hasChildren = 0 < count($this->children); + + if (true === $this->rendered || !$hasChildren) { + return $this->rendered; + } + + if ($hasChildren) { + foreach ($this->children as $child) { + if (!$child->isRendered()) { + return false; + } + } + + return $this->rendered = true; + } + + return false; + } + + /** + * Marks the view as rendered. + * + * @return FormView The view object. + */ + public function setRendered() + { + $this->rendered = true; + + return $this; + } + + /** + * Returns a child by name (implements \ArrayAccess). + * + * @param string $name The child name + * + * @return FormView The child view + */ + public function offsetGet($name) + { + return $this->children[$name]; + } + + /** + * Returns whether the given child exists (implements \ArrayAccess). + * + * @param string $name The child name + * + * @return Boolean Whether the child view exists + */ + public function offsetExists($name) + { + return isset($this->children[$name]); + } + + /** + * Implements \ArrayAccess. + * + * @throws BadMethodCallException always as setting a child by name is not allowed + */ + public function offsetSet($name, $value) + { + throw new BadMethodCallException('Not supported'); + } + + /** + * Removes a child (implements \ArrayAccess). + * + * @param string $name The child name + */ + public function offsetUnset($name) + { + unset($this->children[$name]); + } + + /** + * Returns an iterator to iterate over children (implements \IteratorAggregate) + * + * @return \ArrayIterator The iterator + */ + public function getIterator() + { + return new \ArrayIterator($this->children); + } + + /** + * Implements \Countable. + * + * @return integer The number of children views + */ + public function count() + { + return count($this->children); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Forms.php b/vendor/symfony/form/Symfony/Component/Form/Forms.php new file mode 100644 index 00000000..c949c1f4 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Forms.php @@ -0,0 +1,185 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\Form\Extension\Core\CoreExtension; + +/** + * Entry point of the Form component. + * + * Use this class to conveniently create new form factories: + * + * + * use Symfony\Component\Form\Forms; + * + * $formFactory = Forms::createFormFactory(); + * + * $form = $formFactory->createBuilder() + * ->add('firstName', 'text') + * ->add('lastName', 'text') + * ->add('age', 'integer') + * ->add('gender', 'choice', array( + * 'choices' => array('m' => 'Male', 'f' => 'Female'), + * )) + * ->getForm(); + * + * + * You can also add custom extensions to the form factory: + * + * + * $formFactory = Forms::createFormFactoryBuilder() + * ->addExtension(new AcmeExtension()) + * ->getFormFactory(); + * + * + * If you create custom form types or type extensions, it is + * generally recommended to create your own extensions that lazily + * load these types and type extensions. In projects where performance + * does not matter that much, you can also pass them directly to the + * form factory: + * + * + * $formFactory = Forms::createFormFactoryBuilder() + * ->addType(new PersonType()) + * ->addType(new PhoneNumberType()) + * ->addTypeExtension(new FormTypeHelpTextExtension()) + * ->getFormFactory(); + * + * + * Support for CSRF protection is provided by the CsrfExtension. + * This extension needs a CSRF provider with a strong secret + * (e.g. a 20 character long random string). The default + * implementation for this is DefaultCsrfProvider: + * + * + * use Symfony\Component\Form\Extension\Csrf\CsrfExtension; + * use Symfony\Component\Form\Extension\Csrf\CsrfProvider\DefaultCsrfProvider; + * + * $secret = 'V8a5Z97e...'; + * $formFactory = Forms::createFormFactoryBuilder() + * ->addExtension(new CsrfExtension(new DefaultCsrfProvider($secret))) + * ->getFormFactory(); + * + * + * Support for the HttpFoundation is provided by the + * HttpFoundationExtension. You are also advised to load the CSRF + * extension with the driver for HttpFoundation's Session class: + * + * + * use Symfony\Component\HttpFoundation\Session\Session; + * use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationExtension; + * use Symfony\Component\Form\Extension\Csrf\CsrfExtension; + * use Symfony\Component\Form\Extension\Csrf\CsrfProvider\SessionCsrfProvider; + * + * $session = new Session(); + * $secret = 'V8a5Z97e...'; + * $formFactory = Forms::createFormFactoryBuilder() + * ->addExtension(new HttpFoundationExtension()) + * ->addExtension(new CsrfExtension(new SessionCsrfProvider($session, $secret))) + * ->getFormFactory(); + * + * + * Support for the Validator component is provided by ValidatorExtension. + * This extension needs a validator object to function properly: + * + * + * use Symfony\Component\Validator\Validation; + * use Symfony\Component\Form\Extension\Validator\ValidatorExtension; + * + * $validator = Validation::createValidator(); + * $formFactory = Forms::createFormFactoryBuilder() + * ->addExtension(new ValidatorExtension($validator)) + * ->getFormFactory(); + * + * + * Support for the Templating component is provided by TemplatingExtension. + * This extension needs a PhpEngine object for rendering forms. As second + * argument you should pass the names of the default themes. Here is an + * example for using the default layout with "
" tags: + * + * + * use Symfony\Component\Form\Extension\Templating\TemplatingExtension; + * + * $formFactory = Forms::createFormFactoryBuilder() + * ->addExtension(new TemplatingExtension($engine, null, array( + * 'FrameworkBundle:Form', + * ))) + * ->getFormFactory(); + * + * + * The next example shows how to include the "" layout: + * + * + * use Symfony\Component\Form\Extension\Templating\TemplatingExtension; + * + * $formFactory = Forms::createFormFactoryBuilder() + * ->addExtension(new TemplatingExtension($engine, null, array( + * 'FrameworkBundle:Form', + * 'FrameworkBundle:FormTable', + * ))) + * ->getFormFactory(); + * + * + * If you also loaded the CsrfExtension, you should pass the CSRF provider + * to the extension so that you can render CSRF tokens in your templates + * more easily: + * + * + * use Symfony\Component\Form\Extension\Csrf\CsrfExtension; + * use Symfony\Component\Form\Extension\Csrf\CsrfProvider\DefaultCsrfProvider; + * use Symfony\Component\Form\Extension\Templating\TemplatingExtension; + * + * + * $secret = 'V8a5Z97e...'; + * $csrfProvider = new DefaultCsrfProvider($secret); + * $formFactory = Forms::createFormFactoryBuilder() + * ->addExtension(new CsrfExtension($csrfProvider)) + * ->addExtension(new TemplatingExtension($engine, $csrfProvider, array( + * 'FrameworkBundle:Form', + * ))) + * ->getFormFactory(); + * + * + * @author Bernhard Schussek + */ +final class Forms +{ + /** + * Creates a form factory with the default configuration. + * + * @return FormFactoryInterface The form factory. + */ + public static function createFormFactory() + { + return self::createFormFactoryBuilder()->getFormFactory(); + } + + /** + * Creates a form factory builder with the default configuration. + * + * @return FormFactoryBuilderInterface The form factory builder. + */ + public static function createFormFactoryBuilder() + { + $builder = new FormFactoryBuilder(); + $builder->addExtension(new CoreExtension()); + + return $builder; + } + + /** + * This class cannot be instantiated. + */ + private function __construct() + { + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Guess/Guess.php b/vendor/symfony/form/Symfony/Component/Form/Guess/Guess.php new file mode 100644 index 00000000..b33c3d80 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Guess/Guess.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Guess; + +use Symfony\Component\Form\Exception\InvalidArgumentException; + +/** + * Base class for guesses made by TypeGuesserInterface implementation + * + * Each instance contains a confidence value about the correctness of the guess. + * Thus an instance with confidence HIGH_CONFIDENCE is more likely to be + * correct than an instance with confidence LOW_CONFIDENCE. + * + * @author Bernhard Schussek + */ +abstract class Guess +{ + /** + * Marks an instance with a value that is extremely likely to be correct + * @var integer + */ + const VERY_HIGH_CONFIDENCE = 3; + + /** + * Marks an instance with a value that is very likely to be correct + * @var integer + */ + const HIGH_CONFIDENCE = 2; + + /** + * Marks an instance with a value that is likely to be correct + * @var integer + */ + const MEDIUM_CONFIDENCE = 1; + + /** + * Marks an instance with a value that may be correct + * @var integer + */ + const LOW_CONFIDENCE = 0; + + /** + * The confidence about the correctness of the value + * + * One of VERY_HIGH_CONFIDENCE, HIGH_CONFIDENCE, MEDIUM_CONFIDENCE + * and LOW_CONFIDENCE. + * + * @var integer + */ + private $confidence; + + /** + * Returns the guess most likely to be correct from a list of guesses + * + * If there are multiple guesses with the same, highest confidence, the + * returned guess is any of them. + * + * @param array $guesses A list of guesses + * + * @return Guess The guess with the highest confidence + */ + public static function getBestGuess(array $guesses) + { + $result = null; + $maxConfidence = -1; + + foreach ($guesses as $guess) { + if ($maxConfidence < $confidence = $guess->getConfidence()) { + $maxConfidence = $confidence; + $result = $guess; + } + } + + return $result; + } + + /** + * Constructor + * + * @param integer $confidence The confidence + * + * @throws InvalidArgumentException if the given value of confidence is unknown + */ + public function __construct($confidence) + { + if (self::VERY_HIGH_CONFIDENCE !== $confidence && self::HIGH_CONFIDENCE !== $confidence && + self::MEDIUM_CONFIDENCE !== $confidence && self::LOW_CONFIDENCE !== $confidence) { + throw new InvalidArgumentException('The confidence should be one of the constants defined in Guess.'); + } + + $this->confidence = $confidence; + } + + /** + * Returns the confidence that the guessed value is correct + * + * @return integer One of the constants VERY_HIGH_CONFIDENCE, + * HIGH_CONFIDENCE, MEDIUM_CONFIDENCE and LOW_CONFIDENCE + */ + public function getConfidence() + { + return $this->confidence; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Guess/TypeGuess.php b/vendor/symfony/form/Symfony/Component/Form/Guess/TypeGuess.php new file mode 100644 index 00000000..3241e603 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Guess/TypeGuess.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Guess; + +/** + * Contains a guessed class name and a list of options for creating an instance + * of that class + * + * @author Bernhard Schussek + */ +class TypeGuess extends Guess +{ + /** + * The guessed field type + * @var string + */ + private $type; + + /** + * The guessed options for creating an instance of the guessed class + * @var array + */ + private $options; + + /** + * Constructor + * + * @param string $type The guessed field type + * @param array $options The options for creating instances of the + * guessed class + * @param integer $confidence The confidence that the guessed class name + * is correct + */ + public function __construct($type, array $options, $confidence) + { + parent::__construct($confidence); + + $this->type = $type; + $this->options = $options; + } + + /** + * Returns the guessed field type + * + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * Returns the guessed options for creating instances of the guessed type + * + * @return array + */ + public function getOptions() + { + return $this->options; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Guess/ValueGuess.php b/vendor/symfony/form/Symfony/Component/Form/Guess/ValueGuess.php new file mode 100644 index 00000000..2e3333ba --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Guess/ValueGuess.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Guess; + +/** + * Contains a guessed value + * + * @author Bernhard Schussek + */ +class ValueGuess extends Guess +{ + /** + * The guessed value + * @var array + */ + private $value; + + /** + * Constructor + * + * @param string $value The guessed value + * @param integer $confidence The confidence that the guessed class name + * is correct + */ + public function __construct($value, $confidence) + { + parent::__construct($confidence); + + $this->value = $value; + } + + /** + * Returns the guessed value + * + * @return mixed + */ + public function getValue() + { + return $this->value; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/LICENSE b/vendor/symfony/form/Symfony/Component/Form/LICENSE new file mode 100644 index 00000000..88a57f8d --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2013 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. diff --git a/vendor/symfony/form/Symfony/Component/Form/NativeRequestHandler.php b/vendor/symfony/form/Symfony/Component/Form/NativeRequestHandler.php new file mode 100644 index 00000000..aaa4e4c0 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/NativeRequestHandler.php @@ -0,0 +1,194 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\RequestHandlerInterface; + +/** + * A request handler using PHP's super globals $_GET, $_POST and $_SERVER. + * + * @author Bernhard Schussek + */ +class NativeRequestHandler implements RequestHandlerInterface +{ + /** + * The allowed keys of the $_FILES array. + * + * @var array + */ + private static $fileKeys = array( + 'error', + 'name', + 'size', + 'tmp_name', + 'type', + ); + + /** + * {@inheritdoc} + */ + public function handleRequest(FormInterface $form, $request = null) + { + if (null !== $request) { + throw new UnexpectedTypeException($request, 'null'); + } + + $name = $form->getName(); + $method = $form->getConfig()->getMethod(); + + if ($method !== self::getRequestMethod()) { + return; + } + + if ('GET' === $method) { + if ('' === $name) { + $data = $_GET; + } else { + // Don't submit GET requests if the form's name does not exist + // in the request + if (!isset($_GET[$name])) { + return; + } + + $data = $_GET[$name]; + } + } else { + $fixedFiles = array(); + foreach ($_FILES as $name => $file) { + $fixedFiles[$name] = self::stripEmptyFiles(self::fixPhpFilesArray($file)); + } + + if ('' === $name) { + $params = $_POST; + $files = $fixedFiles; + } else { + $default = $form->getConfig()->getCompound() ? array() : null; + $params = isset($_POST[$name]) ? $_POST[$name] : $default; + $files = isset($fixedFiles[$name]) ? $fixedFiles[$name] : $default; + } + + if (is_array($params) && is_array($files)) { + $data = array_replace_recursive($params, $files); + } else { + $data = $params ?: $files; + } + } + + // Don't auto-submit the form unless at least one field is present. + if ('' === $name && count(array_intersect_key($data, $form->all())) <= 0) { + return; + } + + $form->submit($data, 'PATCH' !== $method); + } + + /** + * Returns the method used to submit the request to the server. + * + * @return string The request method. + */ + private static function getRequestMethod() + { + $method = isset($_SERVER['REQUEST_METHOD']) + ? strtoupper($_SERVER['REQUEST_METHOD']) + : 'GET'; + + if ('POST' === $method && isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) { + $method = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']); + } + + return $method; + } + + /** + * Fixes a malformed PHP $_FILES array. + * + * PHP has a bug that the format of the $_FILES array differs, depending on + * whether the uploaded file fields had normal field names or array-like + * field names ("normal" vs. "parent[child]"). + * + * This method fixes the array to look like the "normal" $_FILES array. + * + * It's safe to pass an already converted array, in which case this method + * just returns the original array unmodified. + * + * This method is identical to {@link Symfony\Component\HttpFoundation\FileBag::fixPhpFilesArray} + * and should be kept as such in order to port fixes quickly and easily. + * + * @param array $data + * + * @return array + */ + private static function fixPhpFilesArray($data) + { + if (!is_array($data)) { + return $data; + } + + $keys = array_keys($data); + sort($keys); + + if (self::$fileKeys !== $keys || !isset($data['name']) || !is_array($data['name'])) { + return $data; + } + + $files = $data; + foreach (self::$fileKeys as $k) { + unset($files[$k]); + } + + foreach (array_keys($data['name']) as $key) { + $files[$key] = self::fixPhpFilesArray(array( + 'error' => $data['error'][$key], + 'name' => $data['name'][$key], + 'type' => $data['type'][$key], + 'tmp_name' => $data['tmp_name'][$key], + 'size' => $data['size'][$key] + )); + } + + return $files; + } + + /** + * Sets empty uploaded files to NULL in the given uploaded files array. + * + * @param mixed $data The file upload data. + * + * @return array|null Returns the stripped upload data. + */ + private static function stripEmptyFiles($data) + { + if (!is_array($data)) { + return $data; + } + + $keys = array_keys($data); + sort($keys); + + if (self::$fileKeys === $keys) { + if (UPLOAD_ERR_NO_FILE === $data['error']) { + return null; + } + + return $data; + } + + foreach ($data as $key => $value) { + $data[$key] = self::stripEmptyFiles($value); + } + + return $data; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/PreloadedExtension.php b/vendor/symfony/form/Symfony/Component/Form/PreloadedExtension.php new file mode 100644 index 00000000..2d3e9efb --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/PreloadedExtension.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\Form\Exception\InvalidArgumentException; + +/** + * A form extension with preloaded types, type exceptions and type guessers. + * + * @author Bernhard Schussek + */ +class PreloadedExtension implements FormExtensionInterface +{ + /** + * @var array + */ + private $types = array(); + + /** + * @var array + */ + private $typeExtensions = array(); + + /** + * @var FormTypeGuesserInterface + */ + private $typeGuesser; + + /** + * Creates a new preloaded extension. + * + * @param array $types The types that the extension should support. + * @param array $typeExtensions The type extensions that the extension should support. + * @param FormTypeGuesserInterface|null $typeGuesser The guesser that the extension should support. + */ + public function __construct(array $types, array $typeExtensions, FormTypeGuesserInterface $typeGuesser = null) + { + $this->types = $types; + $this->typeExtensions = $typeExtensions; + $this->typeGuesser = $typeGuesser; + } + + /** + * {@inheritdoc} + */ + public function getType($name) + { + if (!isset($this->types[$name])) { + throw new InvalidArgumentException(sprintf('The type "%s" can not be loaded by this extension', $name)); + } + + return $this->types[$name]; + } + + /** + * {@inheritdoc} + */ + public function hasType($name) + { + return isset($this->types[$name]); + } + + /** + * {@inheritdoc} + */ + public function getTypeExtensions($name) + { + return isset($this->typeExtensions[$name]) + ? $this->typeExtensions[$name] + : array(); + } + + /** + * {@inheritdoc} + */ + public function hasTypeExtensions($name) + { + return !empty($this->typeExtensions[$name]); + } + + /** + * {@inheritdoc} + */ + public function getTypeGuesser() + { + return $this->typeGuesser; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/README.md b/vendor/symfony/form/Symfony/Component/Form/README.md new file mode 100644 index 00000000..7bfff7fd --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/README.md @@ -0,0 +1,26 @@ +Form Component +============== + +Form provides tools for defining forms, rendering and mapping request data to +related models. Furthermore it provides integration with the Validation +component. + +Resources +--------- + +Silex integration: + +https://github.com/fabpot/Silex/blob/master/src/Silex/Provider/FormServiceProvider.php + +Documentation: + +http://symfony.com/doc/2.3/book/forms.html + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/Form/ + $ composer.phar install --dev + $ phpunit diff --git a/vendor/symfony/form/Symfony/Component/Form/RequestHandlerInterface.php b/vendor/symfony/form/Symfony/Component/Form/RequestHandlerInterface.php new file mode 100644 index 00000000..d0a58e69 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/RequestHandlerInterface.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * Submits forms if they were submitted. + * + * @author Bernhard Schussek + */ +interface RequestHandlerInterface +{ + /** + * Submits a form if it was submitted. + * + * @param FormInterface $form The form to submit. + * @param mixed $request The current request. + */ + public function handleRequest(FormInterface $form, $request = null); +} diff --git a/vendor/symfony/form/Symfony/Component/Form/ResolvedFormType.php b/vendor/symfony/form/Symfony/Component/Form/ResolvedFormType.php new file mode 100644 index 00000000..47d43553 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/ResolvedFormType.php @@ -0,0 +1,284 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\Form\Exception\InvalidArgumentException; +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * A wrapper for a form type and its extensions. + * + * @author Bernhard Schussek + */ +class ResolvedFormType implements ResolvedFormTypeInterface +{ + /** + * @var FormTypeInterface + */ + private $innerType; + + /** + * @var array + */ + private $typeExtensions; + + /** + * @var ResolvedFormTypeInterface + */ + private $parent; + + /** + * @var OptionsResolver + */ + private $optionsResolver; + + public function __construct(FormTypeInterface $innerType, array $typeExtensions = array(), ResolvedFormTypeInterface $parent = null) + { + if (!preg_match('/^[a-z0-9_]*$/i', $innerType->getName())) { + throw new InvalidArgumentException(sprintf( + 'The "%s" form type name ("%s") is not valid. Names must only contain letters, numbers, and "_".', + get_class($innerType), + $innerType->getName() + )); + } + + foreach ($typeExtensions as $extension) { + if (!$extension instanceof FormTypeExtensionInterface) { + throw new UnexpectedTypeException($extension, 'Symfony\Component\Form\FormTypeExtensionInterface'); + } + } + + $this->innerType = $innerType; + $this->typeExtensions = $typeExtensions; + $this->parent = $parent; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->innerType->getName(); + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return $this->parent; + } + + /** + * {@inheritdoc} + */ + public function getInnerType() + { + return $this->innerType; + } + + /** + * {@inheritdoc} + */ + public function getTypeExtensions() + { + // BC + if ($this->innerType instanceof AbstractType) { + return $this->innerType->getExtensions(); + } + + return $this->typeExtensions; + } + + /** + * {@inheritdoc} + */ + public function createBuilder(FormFactoryInterface $factory, $name, array $options = array()) + { + $options = $this->getOptionsResolver()->resolve($options); + + // Should be decoupled from the specific option at some point + $dataClass = isset($options['data_class']) ? $options['data_class'] : null; + + $builder = $this->newBuilder($name, $dataClass, $factory, $options); + $builder->setType($this); + + $this->buildForm($builder, $options); + + return $builder; + } + + /** + * {@inheritdoc} + */ + public function createView(FormInterface $form, FormView $parent = null) + { + $options = $form->getConfig()->getOptions(); + + $view = $this->newView($parent); + + $this->buildView($view, $form, $options); + + foreach ($form as $name => $child) { + /* @var FormInterface $child */ + $view->children[$name] = $child->createView($view); + } + + $this->finishView($view, $form, $options); + + return $view; + } + + /** + * Configures a form builder for the type hierarchy. + * + * This method is protected in order to allow implementing classes + * to change or call it in re-implementations of {@link createBuilder()}. + * + * @param FormBuilderInterface $builder The builder to configure. + * @param array $options The options used for the configuration. + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + if (null !== $this->parent) { + $this->parent->buildForm($builder, $options); + } + + $this->innerType->buildForm($builder, $options); + + foreach ($this->typeExtensions as $extension) { + /* @var FormTypeExtensionInterface $extension */ + $extension->buildForm($builder, $options); + } + } + + /** + * Configures a form view for the type hierarchy. + * + * This method is protected in order to allow implementing classes + * to change or call it in re-implementations of {@link createView()}. + * + * It is called before the children of the view are built. + * + * @param FormView $view The form view to configure. + * @param FormInterface $form The form corresponding to the view. + * @param array $options The options used for the configuration. + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + if (null !== $this->parent) { + $this->parent->buildView($view, $form, $options); + } + + $this->innerType->buildView($view, $form, $options); + + foreach ($this->typeExtensions as $extension) { + /* @var FormTypeExtensionInterface $extension */ + $extension->buildView($view, $form, $options); + } + } + + /** + * Finishes a form view for the type hierarchy. + * + * This method is protected in order to allow implementing classes + * to change or call it in re-implementations of {@link createView()}. + * + * It is called after the children of the view have been built. + * + * @param FormView $view The form view to configure. + * @param FormInterface $form The form corresponding to the view. + * @param array $options The options used for the configuration. + */ + public function finishView(FormView $view, FormInterface $form, array $options) + { + if (null !== $this->parent) { + $this->parent->finishView($view, $form, $options); + } + + $this->innerType->finishView($view, $form, $options); + + foreach ($this->typeExtensions as $extension) { + /* @var FormTypeExtensionInterface $extension */ + $extension->finishView($view, $form, $options); + } + } + + /** + * Returns the configured options resolver used for this type. + * + * This method is protected in order to allow implementing classes + * to change or call it in re-implementations of {@link createBuilder()}. + * + * @return \Symfony\Component\OptionsResolver\OptionsResolverInterface The options resolver. + */ + public function getOptionsResolver() + { + if (null === $this->optionsResolver) { + if (null !== $this->parent) { + $this->optionsResolver = clone $this->parent->getOptionsResolver(); + } else { + $this->optionsResolver = new OptionsResolver(); + } + + $this->innerType->setDefaultOptions($this->optionsResolver); + + foreach ($this->typeExtensions as $extension) { + /* @var FormTypeExtensionInterface $extension */ + $extension->setDefaultOptions($this->optionsResolver); + } + } + + return $this->optionsResolver; + } + + /** + * Creates a new builder instance. + * + * Override this method if you want to customize the builder class. + * + * @param string $name The name of the builder. + * @param string $dataClass The data class. + * @param FormFactoryInterface $factory The current form factory. + * @param array $options The builder options. + * + * @return FormBuilderInterface The new builder instance. + */ + protected function newBuilder($name, $dataClass, FormFactoryInterface $factory, array $options) + { + if ($this->innerType instanceof ButtonTypeInterface) { + return new ButtonBuilder($name, $options); + } + + if ($this->innerType instanceof SubmitButtonTypeInterface) { + return new SubmitButtonBuilder($name, $options); + } + + return new FormBuilder($name, $dataClass, new EventDispatcher(), $factory, $options); + } + + /** + * Creates a new view instance. + * + * Override this method if you want to customize the view class. + * + * @param FormView|null $parent The parent view, if available. + * + * @return FormView A new view instance. + */ + protected function newView(FormView $parent = null) + { + return new FormView($parent); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/ResolvedFormTypeFactory.php b/vendor/symfony/form/Symfony/Component/Form/ResolvedFormTypeFactory.php new file mode 100644 index 00000000..d93d1c06 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/ResolvedFormTypeFactory.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * @author Bernhard Schussek + */ +class ResolvedFormTypeFactory implements ResolvedFormTypeFactoryInterface +{ + /** + * {@inheritdoc} + */ + public function createResolvedType(FormTypeInterface $type, array $typeExtensions, ResolvedFormTypeInterface $parent = null) + { + return new ResolvedFormType($type, $typeExtensions, $parent); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/ResolvedFormTypeFactoryInterface.php b/vendor/symfony/form/Symfony/Component/Form/ResolvedFormTypeFactoryInterface.php new file mode 100644 index 00000000..f0ec2330 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/ResolvedFormTypeFactoryInterface.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * Creates ResolvedFormTypeInterface instances. + * + * This interface allows you to use your custom ResolvedFormTypeInterface + * implementation, within which you can customize the concrete FormBuilderInterface + * implementations or FormView subclasses that are used by the framework. + * + * @author Bernhard Schussek + */ +interface ResolvedFormTypeFactoryInterface +{ + /** + * Resolves a form type. + * + * @param FormTypeInterface $type + * @param array $typeExtensions + * @param ResolvedFormTypeInterface $parent + * + * @return ResolvedFormTypeInterface + * + * @throws Exception\UnexpectedTypeException if the types parent {@link FormTypeInterface::getParent()} is not a string + * @throws Exception\InvalidArgumentException if the types parent can not be retrieved from any extension + */ + public function createResolvedType(FormTypeInterface $type, array $typeExtensions, ResolvedFormTypeInterface $parent = null); +} diff --git a/vendor/symfony/form/Symfony/Component/Form/ResolvedFormTypeInterface.php b/vendor/symfony/form/Symfony/Component/Form/ResolvedFormTypeInterface.php new file mode 100644 index 00000000..c999bcdc --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/ResolvedFormTypeInterface.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * A wrapper for a form type and its extensions. + * + * @author Bernhard Schussek + */ +interface ResolvedFormTypeInterface +{ + /** + * Returns the name of the type. + * + * @return string The type name. + */ + public function getName(); + + /** + * Returns the parent type. + * + * @return ResolvedFormTypeInterface The parent type or null. + */ + public function getParent(); + + /** + * Returns the wrapped form type. + * + * @return FormTypeInterface The wrapped form type. + */ + public function getInnerType(); + + /** + * Returns the extensions of the wrapped form type. + * + * @return FormTypeExtensionInterface[] An array of {@link FormTypeExtensionInterface} instances. + */ + public function getTypeExtensions(); + + /** + * Creates a new form builder for this type. + * + * @param FormFactoryInterface $factory The form factory. + * @param string $name The name for the builder. + * @param array $options The builder options. + * + * @return FormBuilderInterface The created form builder. + */ + public function createBuilder(FormFactoryInterface $factory, $name, array $options = array()); + + /** + * Creates a new form view for a form of this type. + * + * @param FormInterface $form The form to create a view for. + * @param FormView $parent The parent view or null. + * + * @return FormView The created form view. + */ + public function createView(FormInterface $form, FormView $parent = null); + + /** + * Configures a form builder for the type hierarchy. + * + * @param FormBuilderInterface $builder The builder to configure. + * @param array $options The options used for the configuration. + */ + public function buildForm(FormBuilderInterface $builder, array $options); + + /** + * Configures a form view for the type hierarchy. + * + * It is called before the children of the view are built. + * + * @param FormView $view The form view to configure. + * @param FormInterface $form The form corresponding to the view. + * @param array $options The options used for the configuration. + */ + public function buildView(FormView $view, FormInterface $form, array $options); + + /** + * Finishes a form view for the type hierarchy. + * + * It is called after the children of the view have been built. + * + * @param FormView $view The form view to configure. + * @param FormInterface $form The form corresponding to the view. + * @param array $options The options used for the configuration. + */ + public function finishView(FormView $view, FormInterface $form, array $options); + + /** + * Returns the configured options resolver used for this type. + * + * @return \Symfony\Component\OptionsResolver\OptionsResolverInterface The options resolver. + */ + public function getOptionsResolver(); +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/config/validation.xml b/vendor/symfony/form/Symfony/Component/Form/Resources/config/validation.xml new file mode 100644 index 00000000..2f2364bd --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/config/validation.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ar.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ar.xlf new file mode 100644 index 00000000..990b039d --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ar.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + هذا النموذج يجب الا يحتوى على اى حقول اضاÙية. + + + The uploaded file was too large. Please try to upload a smaller file. + مساحة المل٠المرسل كبيرة. من Ùضلك حاول ارسال مل٠اصغر. + + + The CSRF token is invalid. Please try to resubmit the form. + قيمة رمز الموقع غير صحيحة. من Ùضلك اعد ارسال النموذج. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.bg.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.bg.xlf new file mode 100644 index 00000000..6f00bde9 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.bg.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Тази форма не Ñ‚Ñ€Ñбва да Ñъдържа допълнителни полета. + + + The uploaded file was too large. Please try to upload a smaller file. + КачениÑÑ‚ файл е твърде голÑм. МолÑ, опитайте да качите по-малък файл. + + + The CSRF token is invalid. Please try to resubmit the form. + Ðевалиден CSRF токен. МолÑ, опитайте да изпратите формата отново. + + + + \ No newline at end of file diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ca.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ca.xlf new file mode 100644 index 00000000..3a2fa484 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ca.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Aquest formulari no hauria de contenir camps addicionals. + + + The uploaded file was too large. Please try to upload a smaller file. + L'arxiu pujat és massa gran. Per favor, pugi un arxiu més petit. + + + The CSRF token is invalid. Please try to resubmit the form. + El token CSRF no és vàlid. Per favor, provi d'enviar novament el formulari. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.cs.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.cs.xlf new file mode 100644 index 00000000..776da494 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.cs.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Tato skupina polí nesmí obsahovat další pole. + + + The uploaded file was too large. Please try to upload a smaller file. + Nahraný soubor je příliÅ¡ velký. Nahrajte prosím menší soubor. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF token je neplatný. Zkuste prosím znovu odeslat formulář. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.da.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.da.xlf new file mode 100644 index 00000000..c2dd4601 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.da.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Feltgruppen mÃ¥ ikke indeholde ekstra felter. + + + The uploaded file was too large. Please try to upload a smaller file. + Den oploadede fil var for stor. Opload venligst en mindre fil. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF nøglen er ugyldig. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.de.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.de.xlf new file mode 100644 index 00000000..c801d677 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.de.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Dieses Formular sollte keine zusätzlichen Felder enthalten. + + + The uploaded file was too large. Please try to upload a smaller file. + Die hochgeladene Datei ist zu groß. Versuchen Sie bitte eine kleinere Datei hochzuladen. + + + The CSRF token is invalid. Please try to resubmit the form. + Das CSRF-Token ist ungültig. Versuchen Sie bitte das Formular erneut zu senden. + + + + \ No newline at end of file diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.el.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.el.xlf new file mode 100644 index 00000000..ba2ced74 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.el.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Αυτή η φόÏμα δεν Ï€Ïέπει να πεÏιέχει επιπλέον πεδία. + + + The uploaded file was too large. Please try to upload a smaller file. + Το αÏχείο είναι Ï€Î¿Î»Ï Î¼ÎµÎ³Î¬Î»Î¿. ΠαÏακαλοÏμε Ï€Ïοσπαθήστε να ανεβάσετε ένα μικÏότεÏο αÏχείο. + + + The CSRF token is invalid. Please try to resubmit the form. + Το CSRF token δεν είναι έγκυÏο. ΠαÏακαλοÏμε δοκιμάστε να υποβάλετε τη φόÏμα ξανά. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.en.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.en.xlf new file mode 100644 index 00000000..b8542d31 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.en.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + This form should not contain extra fields. + + + The uploaded file was too large. Please try to upload a smaller file. + The uploaded file was too large. Please try to upload a smaller file. + + + The CSRF token is invalid. Please try to resubmit the form. + The CSRF token is invalid. Please try to resubmit the form. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.es.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.es.xlf new file mode 100644 index 00000000..ebfacfd4 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.es.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Este formulario no debería contener campos adicionales. + + + The uploaded file was too large. Please try to upload a smaller file. + El archivo subido es demasiado grande. Por favor, suba un archivo más pequeño. + + + The CSRF token is invalid. Please try to resubmit the form. + El token CSRF no es válido. Por favor, pruebe de enviar nuevamente el formulario + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.et.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.et.xlf new file mode 100644 index 00000000..1a9867fa --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.et.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Väljade grupp ei tohiks sisalda lisaväljasid. + + + The uploaded file was too large. Please try to upload a smaller file. + Ãœleslaaditud fail oli liiga suur. Palun proovi uuesti väiksema failiga. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF-märgis on vigane. Palun proovi vormi uuesti esitada. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.eu.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.eu.xlf new file mode 100644 index 00000000..a07f786c --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.eu.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Formulario honek ez luke aparteko eremurik eduki behar. + + + The uploaded file was too large. Please try to upload a smaller file. + Igotako fitxategia handiegia da. Mesedez saiatu fitxategi txikiago bat igotzen. + + + The CSRF token is invalid. + CSFR tokena ez da egokia. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.fa.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.fa.xlf new file mode 100644 index 00000000..468d2f6f --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.fa.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + این Ùرم نباید Ùیلد اضاÙÛŒ داشته باشد. + + + The uploaded file was too large. Please try to upload a smaller file. + Ùایل بارگذاری شده بسیار بزرگ است. لطÙا Ùایل کوچکتری را بارگزاری کنید. + + + The CSRF token is invalid. Please try to resubmit the form. + مقدار CSRF نامعتبر است. لطÙا Ùرم را مجددا ارسال Ùرمایید.. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.fi.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.fi.xlf new file mode 100644 index 00000000..d223bb05 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.fi.xlf @@ -0,0 +1,19 @@ + + + + + + This field group should not contain extra fields. + Tämä kenttäryhmä ei voi sisältää ylimääräisiä kenttiä. + + + The uploaded file was too large. Please try to upload a smaller file. + Ladattu tiedosto on liian iso. Ole hyvä ja lataa pienempi tiedosto. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF tarkiste on virheellinen. Olen hyvä ja yritä lähettää lomake uudestaan. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.fr.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.fr.xlf new file mode 100644 index 00000000..21f90101 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.fr.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Ce formulaire ne doit pas contenir des champs supplémentaires. + + + The uploaded file was too large. Please try to upload a smaller file. + Le fichier téléchargé est trop volumineux. Merci d'essayer d'envoyer un fichier plus petit. + + + The CSRF token is invalid. Please try to resubmit the form. + Le jeton CSRF est invalide. Veuillez renvoyer le formulaire. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.gl.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.gl.xlf new file mode 100644 index 00000000..db23fe2b --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.gl.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Este formulario non debería conter campos adicionais. + + + The uploaded file was too large. Please try to upload a smaller file. + O arquivo subido é demasiado grande. Por favor, suba un arquivo máis pequeno. + + + The CSRF token is invalid. Please try to resubmit the form. + O token CSRF non é válido. Por favor, probe a enviar novamente o formulario + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.he.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.he.xlf new file mode 100644 index 00000000..5198738d --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.he.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + הטופס ×œ× ×¦×¨×™×š להכיל שדות נוספי×. + + + The uploaded file was too large. Please try to upload a smaller file. + הקובץ שהועלה גדול מדי. נסה להעלות קובץ קטן יותר. + + + The CSRF token is invalid. + ×סימון CSRF ×ינו חוקי. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.hr.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.hr.xlf new file mode 100644 index 00000000..8d0bd292 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.hr.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Ovaj obrazac ne smije sadržavati dodatna polja. + + + The uploaded file was too large. Please try to upload a smaller file. + Prenesena datoteka je prevelika. Molim pokuÅ¡ajte prenijeti manju datoteku. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF vrijednost nije ispravna. PokuÅ¡ajte ponovo poslati obrazac. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.hu.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.hu.xlf new file mode 100644 index 00000000..d1491e7e --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.hu.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Ez a mezÅ‘csoport nem tartalmazhat extra mezÅ‘ket. + + + The uploaded file was too large. Please try to upload a smaller file. + A feltöltött fájl túl nagy. Kérem próbáljon egy kisebb fájlt feltölteni. + + + The CSRF token is invalid. Please try to resubmit the form. + Érvénytelen CSRF token. Kérem próbálja újra elküldeni az űrlapot. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.hy.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.hy.xlf new file mode 100644 index 00000000..5a6091f8 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.hy.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Ô±ÕµÕ½ Õ±Ö‡Õ¨ Õ¹ÕºÕ¥Õ¿Ö„ Õ§ ÕºÕ¡Ö€Õ¸Ö‚Õ¶Õ¡Õ¯Õ« Õ¬Ö€Õ¡ÖÕ¸Ö‚ÖÕ«Õ¹ Õ¿Õ¸Õ²Õ¥Ö€. + + + The uploaded file was too large. Please try to upload a smaller file. + ÕŽÕ¥Ö€Õ¢Õ¥Õ¼Õ¶Õ¾Õ¡Õ® Ö†Õ¡ÕµÕ¬Õ¨ Õ¹Õ¡ÖƒÕ¡Õ¦Õ¡Õ¶Ö Õ´Õ¥Õ® Õ§: Ô½Õ¶Õ¤Ö€Õ¾Õ¸Ö‚Õ´ Õ§ Õ¾Õ¥Ö€Õ¢Õ¥Õ¼Õ¶Õ¥Õ¬ Õ¡Õ¾Õ¥Õ¬Õ« ÖƒÕ¸Ö„Ö€ Õ¹Õ¡ÖƒÕ½Õ« Ö†Õ¡ÕµÕ¬. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF Õ¡Ö€ÕªÕ¥Ö„Õ¨ Õ¡Õ¶Õ©Õ¸Ö‚ÕµÕ¬Õ¡Õ¿Ö€Õ¥Õ¬Õ« Õ§: Õ“Õ¸Ö€Õ±Õ¥Ö„ Õ¶Õ¸Ö€Õ«Ö Õ¸Ö‚Õ²Õ¡Ö€Õ¯Õ¥Õ¬ Õ±Ö‡Õ¨. + + + + \ No newline at end of file diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.id.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.id.xlf new file mode 100644 index 00000000..b067d984 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.id.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Gabungan kolom tidak boleh mengandung kolom tambahan. + + + The uploaded file was too large. Please try to upload a smaller file. + Berkas yang di unggah terlalu besar. Silahkan coba unggah berkas yang lebih kecil. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF-Token tidak sah. Silahkan coba kirim ulang formulir. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.it.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.it.xlf new file mode 100644 index 00000000..aa15264d --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.it.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Questo form non dovrebbe contenere nessun campo extra. + + + The uploaded file was too large. Please try to upload a smaller file. + Il file caricato è troppo grande. Per favore caricare un file più piccolo. + + + The CSRF token is invalid. Please try to resubmit the form. + Il token CSRF non è valido. Provare a reinviare il form. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ja.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ja.xlf new file mode 100644 index 00000000..4559af17 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ja.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + フィールドグループã«è¿½åŠ ã®ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã‚’å«ã‚“ã§ã¯ãªã‚Šã¾ã›ã‚“. + + + The uploaded file was too large. Please try to upload a smaller file. + アップロードã•ã‚ŒãŸãƒ•ã‚¡ã‚¤ãƒ«ãŒå¤§ãã™ãŽã¾ã™ã€‚å°ã•ãªãƒ•ã‚¡ã‚¤ãƒ«ã§å†åº¦ã‚¢ãƒƒãƒ—ロードã—ã¦ãã ã•ã„. + + + The CSRF token is invalid. + CSRFトークンãŒç„¡åŠ¹ã§ã™. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.lb.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.lb.xlf new file mode 100644 index 00000000..a111ffe3 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.lb.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Dës Feldergrupp sollt keng zousätzlech Felder enthalen. + + + The uploaded file was too large. Please try to upload a smaller file. + De geschécktene Fichier ass ze grouss. Versicht wann ech gelift ee méi klenge Fichier eropzelueden. + + + The CSRF token is invalid. Please try to resubmit the form. + Den CSRF-Token ass ongëlteg. Versicht wann ech gelift de Formulaire nach eng Kéier ze schécken. + + + + \ No newline at end of file diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.lt.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.lt.xlf new file mode 100644 index 00000000..25f30887 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.lt.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Forma negali turÄ—ti papildomų laukų. + + + The uploaded file was too large. Please try to upload a smaller file. + Ä®kelta byla yra per didelÄ—. bandykite įkelti mažesnÄ™. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF kodas nepriimtinas. Bandykite siųsti formos užklausÄ… dar kartÄ…. + + + + \ No newline at end of file diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.lv.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.lv.xlf new file mode 100644 index 00000000..9cdfb2cd --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.lv.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Å ajÄ veidlapÄ nevajadzÄ“tu bÅ«t papildus ievades laukiem. + + + The uploaded file was too large. Please try to upload a smaller file. + AugÅ¡upielÄdÄ“tÄ faila izmÄ“rs bija par lielu. LÅ«dzu mÄ“Ä£iniet augÅ¡upielÄdÄ“t mazÄka izmÄ“ra failu. + + + The CSRF token is invalid. Please try to resubmit the form. + Dotais CSRF talons nav derÄ«gs. LÅ«dzu mÄ“Ä£iniet vÄ“lreiz iesniegt veidlapu. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.mn.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.mn.xlf new file mode 100644 index 00000000..002b01c1 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.mn.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Форм нÑмÑлт талбар багтаах боломжгүй. + + + The uploaded file was too large. Please try to upload a smaller file. + Upload хийÑÑн файл Ñ…ÑÑ‚Ñрхий том байна. Бага Ñ…ÑмжÑÑÑ‚Ñй файл оруулна уу. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF token буруу байна. Формоо дахин илгÑÑÐ½Ñ Ò¯Ò¯. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.nb.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.nb.xlf new file mode 100644 index 00000000..5e36bd5f --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.nb.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Feltgruppen mÃ¥ ikke inneholde ekstra felter. + + + The uploaded file was too large. Please try to upload a smaller file. + Den opplastede file var for stor. Vennligst last opp en mindre fil. + + + The CSRF token is invalid. + CSRF nøkkelen er ugyldig. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.nl.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.nl.xlf new file mode 100644 index 00000000..3d737d79 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.nl.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Dit formulier mag geen extra velden bevatten. + + + The uploaded file was too large. Please try to upload a smaller file. + Het geüploade bestand is te groot. Probeer een kleiner bestand te uploaden. + + + The CSRF token is invalid. Please try to resubmit the form. + De CSRF-token is ongeldig. Probeer het formulier opnieuw te versturen. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.pl.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.pl.xlf new file mode 100644 index 00000000..64def2a6 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.pl.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Ten formularz nie powinien zawierać dodatkowych pól. + + + The uploaded file was too large. Please try to upload a smaller file. + Wgrany plik byÅ‚ za duży. ProszÄ™ spróbować wgrać mniejszy plik. + + + The CSRF token is invalid. Please try to resubmit the form. + Token CSRF jest nieprawidÅ‚owy. ProszÄ™ spróbować wysÅ‚ać formularz ponownie. + + + + \ No newline at end of file diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.pt.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.pt.xlf new file mode 100644 index 00000000..554a810c --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.pt.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Este formulário não deveria conter campos extra. + + + The uploaded file was too large. Please try to upload a smaller file. + O arquivo enviado é muito grande. Por favor, tente enviar um ficheiro mais pequeno. + + + The CSRF token is invalid. Please try to resubmit the form. + O token CSRF é inválido. Por favor submeta o formulário novamente. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.pt_BR.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.pt_BR.xlf new file mode 100644 index 00000000..9ae4d719 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.pt_BR.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Este formulário não deve conter campos adicionais. + + + The uploaded file was too large. Please try to upload a smaller file. + O arquivo enviado é muito grande. Por favor, tente enviar um arquivo menor. + + + The CSRF token is invalid. Please try to resubmit the form. + O token CSRF é inválido. Por favor, tente reenviar o formulário. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ro.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ro.xlf new file mode 100644 index 00000000..25abab3b --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ro.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Aceast formular nu ar trebui să conÈ›ină câmpuri suplimentare. + + + The uploaded file was too large. Please try to upload a smaller file. + FiÈ™ierul încărcat a fost prea mare. Vă rugăm sa încărcaÈ›i un fiÈ™ier mai mic. + + + The CSRF token is invalid. Please try to resubmit the form. + Token-ul CSRF este invalid. Vă rugăm să trimiteÈ›i formularul incă o dată. + + + + \ No newline at end of file diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ru.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ru.xlf new file mode 100644 index 00000000..8d829f1b --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ru.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Эта форма не должна Ñодержать дополнительных полей. + + + The uploaded file was too large. Please try to upload a smaller file. + Загруженный файл Ñлишком большой. ПожалуйÑта, попробуйте загрузить файл меньшего размера. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF значение недопуÑтимо. ПожалуйÑта, попробуйте повторить отправку формы. + + + + \ No newline at end of file diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sk.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sk.xlf new file mode 100644 index 00000000..638d0cc4 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sk.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Polia by nemali obsahovaÅ¥ ÄalÅ¡ie prvky. + + + The uploaded file was too large. Please try to upload a smaller file. + Odoslaný súbor je príliÅ¡ veľký. Prosím odoÅ¡lite súbor s menÅ¡ou veľkosÅ¥ou. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF token je neplatný. Prosím skúste znovu odoslaÅ¥ formulár. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sl.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sl.xlf new file mode 100644 index 00000000..103124d0 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sl.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + To podroÄje ne sme vsebovati dodatnih polj. + + + The uploaded file was too large. Please try to upload a smaller file. + Naložena datoteka je prevelika. Prosim, poizkusite naložiti manjÅ¡o. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF vrednost je napaÄna. Prosimo, ponovno poÅ¡ljite obrazec. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sr_Cyrl.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sr_Cyrl.xlf new file mode 100644 index 00000000..ff7f550a --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sr_Cyrl.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Овај формулар не треба да Ñадржи додатна поља. + + + The uploaded file was too large. Please try to upload a smaller file. + Отпремљена датотека је била превелика. Молим покушајте отпремање мање датотеке. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF вредноÑÑ‚ је невалидна. Покушајте поново. + + + + \ No newline at end of file diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sr_Latn.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sr_Latn.xlf new file mode 100644 index 00000000..6c4be358 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sr_Latn.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Ovaj formular ne treba da sadrži dodatna polja. + + + The uploaded file was too large. Please try to upload a smaller file. + Otpremljena datoteka je bila prevelika. Molim pokuÅ¡ajte otpremanje manje datoteke. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF vrednost je nevalidna. PokuÅ¡ajte ponovo. + + + + \ No newline at end of file diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sv.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sv.xlf new file mode 100644 index 00000000..4e2518b1 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sv.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Formuläret kan inte innehÃ¥lla extra fält. + + + The uploaded file was too large. Please try to upload a smaller file. + Den uppladdade filen var för stor. Försök ladda upp en mindre fil. + + + The CSRF token is invalid. + CSRF-symbolen är inte giltig. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ua.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ua.xlf new file mode 100644 index 00000000..4c739fac --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ua.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Ð¦Ñ Ñ„Ð¾Ñ€Ð¼Ð° не повинна міÑтити додаткових полів. + + + The uploaded file was too large. Please try to upload a smaller file. + Завантажений файл занадто великий. Будь-лаÑка, Ñпробуйте завантажити файл меншого розміру. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð½ÐµÐ´Ð¾Ð¿ÑƒÑтиме. Будь-лаÑка, Ñпробуйте відправити форму знову. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.zh_CN.xlf b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.zh_CN.xlf new file mode 100644 index 00000000..8bdf7fb5 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.zh_CN.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + 该表å•ä¸­ä¸å¯æœ‰é¢å¤–字段. + + + The uploaded file was too large. Please try to upload a smaller file. + 上传文件太大, 请é‡æ–°å°è¯•ä¸Šä¼ ä¸€ä¸ªè¾ƒå°çš„文件. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF 验è¯ç¬¦æ— æ•ˆï¼Œ 请é‡æ–°æ交. + + + + diff --git a/vendor/symfony/form/Symfony/Component/Form/ReversedTransformer.php b/vendor/symfony/form/Symfony/Component/Form/ReversedTransformer.php new file mode 100644 index 00000000..7069705f --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/ReversedTransformer.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * Reverses a transformer + * + * When the transform() method is called, the reversed transformer's + * reverseTransform() method is called and vice versa. + * + * @author Bernhard Schussek + */ +class ReversedTransformer implements DataTransformerInterface +{ + /** + * The reversed transformer + * @var DataTransformerInterface + */ + protected $reversedTransformer; + + /** + * Reverses this transformer + * + * @param DataTransformerInterface $reversedTransformer + */ + public function __construct(DataTransformerInterface $reversedTransformer) + { + $this->reversedTransformer = $reversedTransformer; + } + + /** + * {@inheritDoc} + */ + public function transform($value) + { + return $this->reversedTransformer->reverseTransform($value); + } + + /** + * {@inheritDoc} + */ + public function reverseTransform($value) + { + return $this->reversedTransformer->transform($value); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/SubmitButton.php b/vendor/symfony/form/Symfony/Component/Form/SubmitButton.php new file mode 100644 index 00000000..47d4be0e --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/SubmitButton.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * A button that submits the form. + * + * @author Bernhard Schussek + */ +class SubmitButton extends Button implements ClickableInterface +{ + /** + * @var Boolean + */ + private $clicked = false; + + /** + * {@inheritdoc} + */ + public function isClicked() + { + return $this->clicked; + } + + /** + * Submits data to the button. + * + * @param null|string $submittedData The data. + * @param Boolean $clearMissing Not used. + * + * @return SubmitButton The button instance + * + * @throws Exception\AlreadySubmittedException If the form has already been submitted. + */ + public function submit($submittedData, $clearMissing = true) + { + parent::submit($submittedData, $clearMissing); + + $this->clicked = null !== $submittedData; + + return $this; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/SubmitButtonBuilder.php b/vendor/symfony/form/Symfony/Component/Form/SubmitButtonBuilder.php new file mode 100644 index 00000000..088fb748 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/SubmitButtonBuilder.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * A builder for {@link SubmitButton} instances. + * + * @author Bernhard Schussek + */ +class SubmitButtonBuilder extends ButtonBuilder +{ + /** + * Creates the button. + * + * @return Button The button + */ + public function getForm() + { + return new SubmitButton($this->getFormConfig()); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/SubmitButtonTypeInterface.php b/vendor/symfony/form/Symfony/Component/Form/SubmitButtonTypeInterface.php new file mode 100644 index 00000000..f7ac13f7 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/SubmitButtonTypeInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * A type that should be converted into a {@link SubmitButton} instance. + * + * @author Bernhard Schussek + */ +interface SubmitButtonTypeInterface extends FormTypeInterface +{ +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Test/DeprecationErrorHandler.php b/vendor/symfony/form/Symfony/Component/Form/Test/DeprecationErrorHandler.php new file mode 100644 index 00000000..36417f74 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Test/DeprecationErrorHandler.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Test; + +use Symfony\Component\Form\FormEvent; + +class DeprecationErrorHandler +{ + public static function handle($errorNumber, $message, $file, $line, $context) + { + if ($errorNumber & E_USER_DEPRECATED) { + return true; + } + + return \PHPUnit_Util_ErrorHandler::handleError($errorNumber, $message, $file, $line); + } + + public static function handleBC($errorNumber, $message, $file, $line, $context) + { + if ($errorNumber & E_USER_DEPRECATED) { + return true; + } + + return false; + } + + public static function preBind($listener, FormEvent $event) + { + set_error_handler(array('Symfony\Component\Form\Test\DeprecationErrorHandler', 'handle')); + $listener->preBind($event); + restore_error_handler(); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Test/FormBuilderInterface.php b/vendor/symfony/form/Symfony/Component/Form/Test/FormBuilderInterface.php new file mode 100644 index 00000000..711cecd0 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Test/FormBuilderInterface.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Test; + +interface FormBuilderInterface extends \Iterator, \Symfony\Component\Form\FormBuilderInterface +{ +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Test/FormIntegrationTestCase.php b/vendor/symfony/form/Symfony/Component/Form/Test/FormIntegrationTestCase.php new file mode 100644 index 00000000..68e5f244 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Test/FormIntegrationTestCase.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Test; + +use Symfony\Component\Form\Forms; + +/** + * @author Bernhard Schussek + */ +abstract class FormIntegrationTestCase extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Symfony\Component\Form\FormFactoryInterface + */ + protected $factory; + + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + $this->factory = Forms::createFormFactoryBuilder() + ->addExtensions($this->getExtensions()) + ->getFormFactory(); + } + + protected function getExtensions() + { + return array(); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Test/FormInterface.php b/vendor/symfony/form/Symfony/Component/Form/Test/FormInterface.php new file mode 100644 index 00000000..22a83fd7 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Test/FormInterface.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Test; + +interface FormInterface extends \Iterator, \Symfony\Component\Form\FormInterface +{ +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Test/FormPerformanceTestCase.php b/vendor/symfony/form/Symfony/Component/Form/Test/FormPerformanceTestCase.php new file mode 100644 index 00000000..573f4e91 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Test/FormPerformanceTestCase.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Test; + +/** + * Base class for performance tests. + * + * Copied from Doctrine 2's OrmPerformanceTestCase. + * + * @author robo + * @author Bernhard Schussek + */ +abstract class FormPerformanceTestCase extends FormIntegrationTestCase +{ + /** + * @var integer + */ + protected $maxRunningTime = 0; + + /** + */ + protected function runTest() + { + $s = microtime(true); + parent::runTest(); + $time = microtime(true) - $s; + + if ($this->maxRunningTime != 0 && $time > $this->maxRunningTime) { + $this->fail( + sprintf( + 'expected running time: <= %s but was: %s', + + $this->maxRunningTime, + $time + ) + ); + } + } + + /** + * @param integer $maxRunningTime + * @throws \InvalidArgumentException + */ + public function setMaxRunningTime($maxRunningTime) + { + if (is_integer($maxRunningTime) && $maxRunningTime >= 0) { + $this->maxRunningTime = $maxRunningTime; + } else { + throw new \InvalidArgumentException; + } + } + + /** + * @return integer + * @since Method available since Release 2.3.0 + */ + public function getMaxRunningTime() + { + return $this->maxRunningTime; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Test/TypeTestCase.php b/vendor/symfony/form/Symfony/Component/Form/Test/TypeTestCase.php new file mode 100644 index 00000000..9d51a9ee --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Test/TypeTestCase.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Test; + +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\EventDispatcher\EventDispatcher; + +abstract class TypeTestCase extends FormIntegrationTestCase +{ + /** + * @var FormBuilder + */ + protected $builder; + + /** + * @var EventDispatcher + */ + protected $dispatcher; + + protected function setUp() + { + parent::setUp(); + + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->builder = new FormBuilder(null, null, $this->dispatcher, $this->factory); + } + + public static function assertDateTimeEquals(\DateTime $expected, \DateTime $actual) + { + self::assertEquals($expected->format('c'), $actual->format('c')); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php new file mode 100644 index 00000000..ee9ed8f2 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php @@ -0,0 +1,732 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\FormError; +use Symfony\Component\Form\Tests\Fixtures\AlternatingRowType; + +abstract class AbstractDivLayoutTest extends AbstractLayoutTest +{ + public function testRow() + { + $form = $this->factory->createNamed('name', 'text'); + $form->addError(new FormError('[trans]Error![/trans]')); + $view = $form->createView(); + $html = $this->renderRow($view); + + $this->assertMatchesXpath($html, +'/div + [ + ./label[@for="name"] + /following-sibling::ul + [./li[.="[trans]Error![/trans]"]] + [count(./li)=1] + /following-sibling::input[@id="name"] + ] +' + ); + } + + public function testRowOverrideVariables() + { + $view = $this->factory->createNamed('name', 'text')->createView(); + $html = $this->renderRow($view, array( + 'attr' => array('class' => 'my&class'), + 'label' => 'foo&bar', + 'label_attr' => array('class' => 'my&label&class'), + )); + + $this->assertMatchesXpath($html, +'/div + [ + ./label[@for="name"][@class="my&label&class required"][.="[trans]foo&bar[/trans]"] + /following-sibling::input[@id="name"][@class="my&class"] + ] +' + ); + } + + public function testRepeatedRow() + { + $form = $this->factory->createNamed('name', 'repeated'); + $form->addError(new FormError('[trans]Error![/trans]')); + $view = $form->createView(); + $html = $this->renderRow($view); + + // The errors of the form are not rendered by intention! + // In practice, repeated fields cannot have errors as all errors + // on them are mapped to the first child. + // (see RepeatedTypeValidatorExtension) + + $this->assertMatchesXpath($html, +'/div + [ + ./label[@for="name_first"] + /following-sibling::input[@id="name_first"] + ] +/following-sibling::div + [ + ./label[@for="name_second"] + /following-sibling::input[@id="name_second"] + ] +' + ); + } + + public function testButtonRow() + { + $form = $this->factory->createNamed('name', 'button'); + $view = $form->createView(); + $html = $this->renderRow($view); + + $this->assertMatchesXpath($html, +'/div + [ + ./button[@type="button"][@name="name"] + ] + [count(//label)=0] +' + ); + } + + public function testRest() + { + $view = $this->factory->createNamedBuilder('name', 'form') + ->add('field1', 'text') + ->add('field2', 'repeated') + ->add('field3', 'text') + ->add('field4', 'text') + ->getForm() + ->createView(); + + // Render field2 row -> does not implicitly call renderWidget because + // it is a repeated field! + $this->renderRow($view['field2']); + + // Render field3 widget + $this->renderWidget($view['field3']); + + // Rest should only contain field1 and field4 + $html = $this->renderRest($view); + + $this->assertMatchesXpath($html, +'/div + [ + ./label[@for="name_field1"] + /following-sibling::input[@type="text"][@id="name_field1"] + ] +/following-sibling::div + [ + ./label[@for="name_field4"] + /following-sibling::input[@type="text"][@id="name_field4"] + ] + [count(../div)=2] + [count(..//label)=2] + [count(..//input)=3] +/following-sibling::input + [@type="hidden"] + [@id="name__token"] +' + ); + } + + public function testRestWithChildrenForms() + { + $child1 = $this->factory->createNamedBuilder('child1', 'form') + ->add('field1', 'text') + ->add('field2', 'text'); + + $child2 = $this->factory->createNamedBuilder('child2', 'form') + ->add('field1', 'text') + ->add('field2', 'text'); + + $view = $this->factory->createNamedBuilder('parent', 'form') + ->add($child1) + ->add($child2) + ->getForm() + ->createView(); + + // Render child1.field1 row + $this->renderRow($view['child1']['field1']); + + // Render child2.field2 widget (remember that widget don't render label) + $this->renderWidget($view['child2']['field2']); + + // Rest should only contain child1.field2 and child2.field1 + $html = $this->renderRest($view); + + $this->assertMatchesXpath($html, +'/div + [ + ./label[not(@for)] + /following-sibling::div[@id="parent_child1"] + [ + ./div + [ + ./label[@for="parent_child1_field2"] + /following-sibling::input[@id="parent_child1_field2"] + ] + ] + ] + +/following-sibling::div + [ + ./label[not(@for)] + /following-sibling::div[@id="parent_child2"] + [ + ./div + [ + ./label[@for="parent_child2_field1"] + /following-sibling::input[@id="parent_child2_field1"] + ] + ] + ] + [count(//label)=4] + [count(//input[@type="text"])=2] +/following-sibling::input[@type="hidden"][@id="parent__token"] +' + ); + } + + public function testRestAndRepeatedWithRow() + { + $view = $this->factory->createNamedBuilder('name', 'form') + ->add('first', 'text') + ->add('password', 'repeated') + ->getForm() + ->createView(); + + $this->renderRow($view['password']); + + $html = $this->renderRest($view); + + $this->assertMatchesXpath($html, +'/div + [ + ./label[@for="name_first"] + /following-sibling::input[@type="text"][@id="name_first"] + ] + [count(.//input)=1] +/following-sibling::input + [@type="hidden"] + [@id="name__token"] +' + ); + } + + public function testRestAndRepeatedWithRowPerChild() + { + $view = $this->factory->createNamedBuilder('name', 'form') + ->add('first', 'text') + ->add('password', 'repeated') + ->getForm() + ->createView(); + + $this->renderRow($view['password']['first']); + $this->renderRow($view['password']['second']); + + $html = $this->renderRest($view); + + $this->assertMatchesXpath($html, +'/div + [ + ./label[@for="name_first"] + /following-sibling::input[@type="text"][@id="name_first"] + ] + [count(.//input)=1] + [count(.//label)=1] +/following-sibling::input + [@type="hidden"] + [@id="name__token"] +' + ); + } + + public function testRestAndRepeatedWithWidgetPerChild() + { + $view = $this->factory->createNamedBuilder('name', 'form') + ->add('first', 'text') + ->add('password', 'repeated') + ->getForm() + ->createView(); + + // The password form is considered as rendered as all its children + // are rendered + $this->renderWidget($view['password']['first']); + $this->renderWidget($view['password']['second']); + + $html = $this->renderRest($view); + + $this->assertMatchesXpath($html, +'/div + [ + ./label[@for="name_first"] + /following-sibling::input[@type="text"][@id="name_first"] + ] + [count(//input)=2] + [count(//label)=1] +/following-sibling::input + [@type="hidden"] + [@id="name__token"] +' + ); + } + + public function testCollection() + { + $form = $this->factory->createNamed('name', 'collection', array('a', 'b'), array( + 'type' => 'text', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div[./input[@type="text"][@value="a"]] + /following-sibling::div[./input[@type="text"][@value="b"]] + ] + [count(./div[./input])=2] +' + ); + } + + // https://github.com/symfony/symfony/issues/5038 + public function testCollectionWithAlternatingRowTypes() + { + $data = array( + array('title' => 'a'), + array('title' => 'b'), + ); + $form = $this->factory->createNamed('name', 'collection', $data, array( + 'type' => new AlternatingRowType(), + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div[./div/div/input[@type="text"][@value="a"]] + /following-sibling::div[./div/div/textarea[.="b"]] + ] + [count(./div[./div/div/input])=1] + [count(./div[./div/div/textarea])=1] +' + ); + } + + public function testEmptyCollection() + { + $form = $this->factory->createNamed('name', 'collection', array(), array( + 'type' => 'text', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [./input[@type="hidden"][@id="name__token"]] + [count(./div)=0] +' + ); + } + + public function testCollectionRow() + { + $collection = $this->factory->createNamedBuilder( + 'collection', + 'collection', + array('a', 'b'), + array('type' => 'text') + ); + + $form = $this->factory->createNamedBuilder('form', 'form') + ->add($collection) + ->getForm(); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [ + ./label[not(@for)] + /following-sibling::div + [ + ./div + [ + ./label[@for="form_collection_0"] + /following-sibling::input[@type="text"][@value="a"] + ] + /following-sibling::div + [ + ./label[@for="form_collection_1"] + /following-sibling::input[@type="text"][@value="b"] + ] + ] + ] + /following-sibling::input[@type="hidden"][@id="form__token"] + ] + [count(.//input)=3] +' + ); + } + + public function testForm() + { + $form = $this->factory->createNamedBuilder('name', 'form') + ->setMethod('PUT') + ->setAction('http://example.com') + ->add('firstName', 'text') + ->add('lastName', 'text') + ->getForm(); + + // include ampersands everywhere to validate escaping + $html = $this->renderForm($form->createView(), array( + 'id' => 'my&id', + 'attr' => array('class' => 'my&class'), + )); + + $this->assertMatchesXpath($html, +'/form + [ + ./input[@type="hidden"][@name="_method"][@value="PUT"] + /following-sibling::div + [ + ./div + [ + ./label[@for="name_firstName"] + /following-sibling::input[@type="text"][@id="name_firstName"] + ] + /following-sibling::div + [ + ./label[@for="name_lastName"] + /following-sibling::input[@type="text"][@id="name_lastName"] + ] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(.//input)=3] + [@id="my&id"] + [@class="my&class"] + ] + [@method="post"] + [@action="http://example.com"] + [@class="my&class"] +' + ); + } + + public function testFormWidget() + { + $form = $this->factory->createNamedBuilder('name', 'form') + ->add('firstName', 'text') + ->add('lastName', 'text') + ->getForm(); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [ + ./label[@for="name_firstName"] + /following-sibling::input[@type="text"][@id="name_firstName"] + ] + /following-sibling::div + [ + ./label[@for="name_lastName"] + /following-sibling::input[@type="text"][@id="name_lastName"] + ] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(.//input)=3] +' + ); + } + + // https://github.com/symfony/symfony/issues/2308 + public function testNestedFormError() + { + $form = $this->factory->createNamedBuilder('name', 'form') + ->add($this->factory + ->createNamedBuilder('child', 'form', null, array('error_bubbling' => false)) + ->add('grandChild', 'form') + ) + ->getForm(); + + $form->get('child')->addError(new FormError('[trans]Error![/trans]')); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div/label + /following-sibling::ul[./li[.="[trans]Error![/trans]"]] + ] + [count(.//li[.="[trans]Error![/trans]"])=1] +' + ); + } + + public function testCsrf() + { + $this->csrfProvider->expects($this->any()) + ->method('generateCsrfToken') + ->will($this->returnValue('foo&bar')); + + $form = $this->factory->createNamedBuilder('name', 'form') + ->add($this->factory + // No CSRF protection on nested forms + ->createNamedBuilder('child', 'form') + ->add($this->factory->createNamedBuilder('grandchild', 'text')) + ) + ->getForm(); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + /following-sibling::input[@type="hidden"][@id="name__token"][@value="foo&bar"] + ] + [count(.//input[@type="hidden"])=1] +' + ); + } + + public function testRepeated() + { + $form = $this->factory->createNamed('name', 'repeated', 'foobar', array( + 'type' => 'text', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [ + ./label[@for="name_first"] + /following-sibling::input[@type="text"][@id="name_first"] + ] + /following-sibling::div + [ + ./label[@for="name_second"] + /following-sibling::input[@type="text"][@id="name_second"] + ] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(.//input)=3] +' + ); + } + + public function testRepeatedWithCustomOptions() + { + $form = $this->factory->createNamed('name', 'repeated', null, array( + // the global required value cannot be overridden + 'first_options' => array('label' => 'Test', 'required' => false), + 'second_options' => array('label' => 'Test2') + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [ + ./label[@for="name_first"][.="[trans]Test[/trans]"] + /following-sibling::input[@type="text"][@id="name_first"][@required="required"] + ] + /following-sibling::div + [ + ./label[@for="name_second"][.="[trans]Test2[/trans]"] + /following-sibling::input[@type="text"][@id="name_second"][@required="required"] + ] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(.//input)=3] +' + ); + } + + public function testSearchInputName() + { + $form = $this->factory->createNamedBuilder('full', 'form') + ->add('name', 'search') + ->getForm(); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [ + ./label[@for="full_name"] + /following-sibling::input[@type="search"][@id="full_name"][@name="full[name]"] + ] + /following-sibling::input[@type="hidden"][@id="full__token"] + ] + [count(//input)=2] +' + ); + } + + public function testLabelHasNoId() + { + $form = $this->factory->createNamed('name', 'text'); + $html = $this->renderRow($form->createView()); + + $this->assertMatchesXpath($html, +'/div + [ + ./label[@for="name"][not(@id)] + /following-sibling::input[@id="name"] + ] +' + ); + } + + public function testLabelIsNotRenderedWhenSetToFalse() + { + $form = $this->factory->createNamed('name', 'text', null, array( + 'label' => false + )); + $html = $this->renderRow($form->createView()); + + $this->assertMatchesXpath($html, +'/div + [ + ./input[@id="name"] + ] + [count(//label)=0] +' + ); + } + + /** + * @dataProvider themeBlockInheritanceProvider + */ + public function testThemeBlockInheritance($theme) + { + $view = $this->factory + ->createNamed('name', 'email') + ->createView() + ; + + $this->setTheme($view, $theme); + + $this->assertMatchesXpath( + $this->renderWidget($view), + '/input[@type="email"][@rel="theme"]' + ); + } + + /** + * @dataProvider themeInheritanceProvider + */ + public function testThemeInheritance($parentTheme, $childTheme) + { + $child = $this->factory->createNamedBuilder('child', 'form') + ->add('field', 'text'); + + $view = $this->factory->createNamedBuilder('parent', 'form') + ->add('field', 'text') + ->add($child) + ->getForm() + ->createView() + ; + + $this->setTheme($view, $parentTheme); + $this->setTheme($view['child'], $childTheme); + + $this->assertWidgetMatchesXpath($view, array(), +'/div + [ + ./div + [ + ./label[.="parent"] + /following-sibling::input[@type="text"] + ] + /following-sibling::div + [ + ./label[.="child"] + /following-sibling::div + [ + ./div + [ + ./label[.="child"] + /following-sibling::input[@type="text"] + ] + ] + ] + /following-sibling::input[@type="hidden"] + ] +' + ); + } + + /** + * The block "_name_child_label" should be overridden in the theme of the + * implemented driver. + */ + public function testCollectionRowWithCustomBlock() + { + $collection = array('one', 'two', 'three'); + $form = $this->factory->createNamedBuilder('name', 'collection', $collection) + ->getForm(); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div[./label[.="Custom label: [trans]0[/trans]"]] + /following-sibling::div[./label[.="Custom label: [trans]1[/trans]"]] + /following-sibling::div[./label[.="Custom label: [trans]2[/trans]"]] + ] +' + ); + } + + public function testFormEndWithRest() + { + $view = $this->factory->createNamedBuilder('name', 'form') + ->add('field1', 'text') + ->add('field2', 'text') + ->getForm() + ->createView(); + + $this->renderWidget($view['field1']); + + // Rest should only contain field2 + $html = $this->renderEnd($view); + + // Insert the start tag, the end tag should be rendered by the helper + $this->assertMatchesXpath('
' . $html, +'/form + [ + ./div + [ + ./label[@for="name_field2"] + /following-sibling::input[@type="text"][@id="name_field2"] + ] + /following-sibling::input + [@type="hidden"] + [@id="name__token"] + ] +' + ); + } + + public function testFormEndWithoutRest() + { + $view = $this->factory->createNamedBuilder('name', 'form') + ->add('field1', 'text') + ->add('field2', 'text') + ->getForm() + ->createView(); + + $this->renderWidget($view['field1']); + + // Rest should only contain field2, but isn't rendered + $html = $this->renderEnd($view, array('render_rest' => false)); + + $this->assertEquals('', $html); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/AbstractExtensionTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/AbstractExtensionTest.php new file mode 100644 index 00000000..c16cb221 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/AbstractExtensionTest.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\AbstractExtension; +use Symfony\Component\Form\Tests\Fixtures\FooType; + +class AbstractExtensionTest extends \PHPUnit_Framework_TestCase +{ + public function testHasType() + { + $loader = new ConcreteExtension(); + $this->assertTrue($loader->hasType('foo')); + $this->assertFalse($loader->hasType('bar')); + } + + public function testGetType() + { + $loader = new ConcreteExtension(); + $this->assertInstanceOf('Symfony\Component\Form\Tests\Fixtures\FooType', $loader->getType('foo')); + } +} + +class ConcreteExtension extends AbstractExtension +{ + protected function loadTypes() + { + return array(new FooType()); + } + + protected function loadTypeGuesser() + { + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/AbstractFormTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/AbstractFormTest.php new file mode 100644 index 00000000..29118187 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/AbstractFormTest.php @@ -0,0 +1,147 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +abstract class AbstractFormTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var EventDispatcherInterface + */ + protected $dispatcher; + + /** + * @var \Symfony\Component\Form\FormFactoryInterface + */ + protected $factory; + + /** + * @var \Symfony\Component\Form\FormInterface + */ + protected $form; + + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + // We need an actual dispatcher to use the deprecated + // bindRequest() method + $this->dispatcher = new EventDispatcher(); + $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); + $this->form = $this->createForm(); + } + + protected function tearDown() + { + $this->dispatcher = null; + $this->factory = null; + $this->form = null; + } + + /** + * @return \Symfony\Component\Form\FormInterface + */ + abstract protected function createForm(); + + /** + * @param string $name + * @param EventDispatcherInterface $dispatcher + * @param string $dataClass + * + * @return FormBuilder + */ + protected function getBuilder($name = 'name', EventDispatcherInterface $dispatcher = null, $dataClass = null) + { + return new FormBuilder($name, $dataClass, $dispatcher ?: $this->dispatcher, $this->factory); + } + + /** + * @param string $name + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + protected function getMockForm($name = 'name') + { + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $config = $this->getMock('Symfony\Component\Form\FormConfigInterface'); + + $form->expects($this->any()) + ->method('getName') + ->will($this->returnValue($name)); + $form->expects($this->any()) + ->method('getConfig') + ->will($this->returnValue($config)); + + return $form; + } + + /** + * @param string $name + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + protected function getValidForm($name) + { + $form = $this->getMockForm($name); + + $form->expects($this->any()) + ->method('isValid') + ->will($this->returnValue(true)); + + return $form; + } + + /** + * @param string $name + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + protected function getInvalidForm($name) + { + $form = $this->getMockForm($name); + + $form->expects($this->any()) + ->method('isValid') + ->will($this->returnValue(false)); + + return $form; + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + protected function getDataMapper() + { + return $this->getMock('Symfony\Component\Form\DataMapperInterface'); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + protected function getDataTransformer() + { + return $this->getMock('Symfony\Component\Form\DataTransformerInterface'); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + protected function getFormValidator() + { + return $this->getMock('Symfony\Component\Form\FormValidatorInterface'); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/AbstractLayoutTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/AbstractLayoutTest.php new file mode 100644 index 00000000..8f632a2b --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/AbstractLayoutTest.php @@ -0,0 +1,1876 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\FormError; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\Extension\Csrf\CsrfExtension; + +abstract class AbstractLayoutTest extends \Symfony\Component\Form\Test\FormIntegrationTestCase +{ + protected $csrfProvider; + + protected function setUp() + { + if (!extension_loaded('intl')) { + $this->markTestSkipped('The "intl" extension is not available'); + } + + \Locale::setDefault('en'); + + $this->csrfProvider = $this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface'); + + parent::setUp(); + } + + protected function getExtensions() + { + return array( + new CsrfExtension($this->csrfProvider), + ); + } + + protected function tearDown() + { + $this->csrfProvider = null; + + parent::tearDown(); + } + + protected function assertXpathNodeValue(\DomElement $element, $expression, $nodeValue) + { + $xpath = new \DOMXPath($element->ownerDocument); + $nodeList = $xpath->evaluate($expression); + $this->assertEquals(1, $nodeList->length); + $this->assertEquals($nodeValue, $nodeList->item(0)->nodeValue); + } + + protected function assertMatchesXpath($html, $expression, $count = 1) + { + $dom = new \DomDocument('UTF-8'); + try { + // Wrap in node so we can load HTML with multiple tags at + // the top level + $dom->loadXml(''.$html.''); + } catch (\Exception $e) { + $this->fail(sprintf( + "Failed loading HTML:\n\n%s\n\nError: %s", + $html, + $e->getMessage() + )); + } + $xpath = new \DOMXPath($dom); + $nodeList = $xpath->evaluate('/root'.$expression); + + if ($nodeList->length != $count) { + $dom->formatOutput = true; + $this->fail(sprintf( + "Failed asserting that \n\n%s\n\nmatches exactly %s. Matches %s in \n\n%s", + $expression, + $count == 1 ? 'once' : $count.' times', + $nodeList->length == 1 ? 'once' : $nodeList->length.' times', + // strip away and + substr($dom->saveHTML(), 6, -8) + )); + } + } + + protected function assertWidgetMatchesXpath(FormView $view, array $vars, $xpath) + { + // include ampersands everywhere to validate escaping + $html = $this->renderWidget($view, array_merge(array( + 'id' => 'my&id', + 'attr' => array('class' => 'my&class'), + ), $vars)); + + $xpath = trim($xpath).' + [@id="my&id"] + [@class="my&class"]'; + + $this->assertMatchesXpath($html, $xpath); + } + + abstract protected function renderForm(FormView $view, array $vars = array()); + + abstract protected function renderEnctype(FormView $view); + + abstract protected function renderLabel(FormView $view, $label = null, array $vars = array()); + + abstract protected function renderErrors(FormView $view); + + abstract protected function renderWidget(FormView $view, array $vars = array()); + + abstract protected function renderRow(FormView $view, array $vars = array()); + + abstract protected function renderRest(FormView $view, array $vars = array()); + + abstract protected function renderStart(FormView $view, array $vars = array()); + + abstract protected function renderEnd(FormView $view, array $vars = array()); + + abstract protected function setTheme(FormView $view, array $themes); + + public function testEnctype() + { + $form = $this->factory->createNamedBuilder('name', 'form') + ->add('file', 'file') + ->getForm(); + + $this->assertEquals('enctype="multipart/form-data"', $this->renderEnctype($form->createView())); + } + + public function testNoEnctype() + { + $form = $this->factory->createNamedBuilder('name', 'form') + ->add('text', 'text') + ->getForm(); + + $this->assertEquals('', $this->renderEnctype($form->createView())); + } + + public function testLabel() + { + $form = $this->factory->createNamed('name', 'text'); + $view = $form->createView(); + $this->renderWidget($view, array('label' => 'foo')); + $html = $this->renderLabel($view); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [.="[trans]Name[/trans]"] +' + ); + } + + public function testLabelOnForm() + { + $form = $this->factory->createNamed('name', 'date'); + $view = $form->createView(); + $this->renderWidget($view, array('label' => 'foo')); + $html = $this->renderLabel($view); + + $this->assertMatchesXpath($html, +'/label + [@class="required"] + [.="[trans]Name[/trans]"] +' + ); + } + + public function testLabelWithCustomTextPassedAsOption() + { + $form = $this->factory->createNamed('name', 'text', null, array( + 'label' => 'Custom label', + )); + $html = $this->renderLabel($form->createView()); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [.="[trans]Custom label[/trans]"] +' + ); + } + + public function testLabelWithCustomTextPassedDirectly() + { + $form = $this->factory->createNamed('name', 'text'); + $html = $this->renderLabel($form->createView(), 'Custom label'); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [.="[trans]Custom label[/trans]"] +' + ); + } + + public function testLabelWithCustomTextPassedAsOptionAndDirectly() + { + $form = $this->factory->createNamed('name', 'text', null, array( + 'label' => 'Custom label', + )); + $html = $this->renderLabel($form->createView(), 'Overridden label'); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [.="[trans]Overridden label[/trans]"] +' + ); + } + + public function testLabelDoesNotRenderFieldAttributes() + { + $form = $this->factory->createNamed('name', 'text'); + $html = $this->renderLabel($form->createView(), null, array( + 'attr' => array( + 'class' => 'my&class' + ), + )); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [@class="required"] +' + ); + } + + public function testLabelWithCustomAttributesPassedDirectly() + { + $form = $this->factory->createNamed('name', 'text'); + $html = $this->renderLabel($form->createView(), null, array( + 'label_attr' => array( + 'class' => 'my&class' + ), + )); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [@class="my&class required"] +' + ); + } + + public function testLabelWithCustomTextAndCustomAttributesPassedDirectly() + { + $form = $this->factory->createNamed('name', 'text'); + $html = $this->renderLabel($form->createView(), 'Custom label', array( + 'label_attr' => array( + 'class' => 'my&class' + ), + )); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [@class="my&class required"] + [.="[trans]Custom label[/trans]"] +' + ); + } + + // https://github.com/symfony/symfony/issues/5029 + public function testLabelWithCustomTextAsOptionAndCustomAttributesPassedDirectly() + { + $form = $this->factory->createNamed('name', 'text', null, array( + 'label' => 'Custom label', + )); + $html = $this->renderLabel($form->createView(), null, array( + 'label_attr' => array( + 'class' => 'my&class' + ), + )); + + $this->assertMatchesXpath($html, + '/label + [@for="name"] + [@class="my&class required"] + [.="[trans]Custom label[/trans]"] +' + ); + } + + public function testErrors() + { + $form = $this->factory->createNamed('name', 'text'); + $form->addError(new FormError('[trans]Error 1[/trans]')); + $form->addError(new FormError('[trans]Error 2[/trans]')); + $view = $form->createView(); + $html = $this->renderErrors($view); + + $this->assertMatchesXpath($html, +'/ul + [ + ./li[.="[trans]Error 1[/trans]"] + /following-sibling::li[.="[trans]Error 2[/trans]"] + ] + [count(./li)=2] +' + ); + } + + public function testWidgetById() + { + $form = $this->factory->createNamed('text_id', 'text'); + $html = $this->renderWidget($form->createView()); + + $this->assertMatchesXpath($html, +'/div + [ + ./input + [@type="text"] + [@id="text_id"] + ] + [@id="container"] +' + ); + } + + public function testCheckedCheckbox() + { + $form = $this->factory->createNamed('name', 'checkbox', true); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="checkbox"] + [@name="name"] + [@checked="checked"] + [@value="1"] +' + ); + } + + public function testUncheckedCheckbox() + { + $form = $this->factory->createNamed('name', 'checkbox', false); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="checkbox"] + [@name="name"] + [not(@checked)] +' + ); + } + + public function testCheckboxWithValue() + { + $form = $this->factory->createNamed('name', 'checkbox', false, array( + 'value' => 'foo&bar', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="checkbox"] + [@name="name"] + [@value="foo&bar"] +' + ); + } + + public function testSingleChoice() + { + $form = $this->factory->createNamed('name', 'choice', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [@required="required"] + [ + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] +' + ); + } + + public function testSingleChoiceWithPreferred() + { + $form = $this->factory->createNamed('name', 'choice', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'preferred_choices' => array('&b'), + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('separator' => '-- sep --'), +'/select + [@name="name"] + [@required="required"] + [ + ./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + /following-sibling::option[@disabled="disabled"][not(@selected)][.="-- sep --"] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + ] + [count(./option)=3] +' + ); + } + + public function testSingleChoiceWithPreferredAndNoSeparator() + { + $form = $this->factory->createNamed('name', 'choice', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'preferred_choices' => array('&b'), + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('separator' => null), +'/select + [@name="name"] + [@required="required"] + [ + ./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + ] + [count(./option)=2] +' + ); + } + + public function testSingleChoiceWithPreferredAndBlankSeparator() + { + $form = $this->factory->createNamed('name', 'choice', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'preferred_choices' => array('&b'), + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('separator' => ''), +'/select + [@name="name"] + [@required="required"] + [ + ./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + /following-sibling::option[@disabled="disabled"][not(@selected)][.=""] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + ] + [count(./option)=3] +' + ); + } + + public function testChoiceWithOnlyPreferred() + { + $form = $this->factory->createNamed('name', 'choice', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'preferred_choices' => array('&a', '&b'), + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [count(./option)=2] +' + ); + } + + public function testSingleChoiceNonRequired() + { + $form = $this->factory->createNamed('name', 'choice', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'required' => false, + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [not(@required)] + [ + ./option[@value=""][.="[trans][/trans]"] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=3] +' + ); + } + + public function testSingleChoiceNonRequiredNoneSelected() + { + $form = $this->factory->createNamed('name', 'choice', null, array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'required' => false, + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [not(@required)] + [ + ./option[@value=""][.="[trans][/trans]"] + /following-sibling::option[@value="&a"][not(@selected)][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=3] +' + ); + } + + public function testSingleChoiceWithNonRequiredEmptyValue() + { + $form = $this->factory->createNamed('name', 'choice', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'multiple' => false, + 'expanded' => false, + 'required' => false, + 'empty_value' => 'Select&Anything&Not&Me', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [not(@required)] + [ + ./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Select&Anything&Not&Me[/trans]"] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=3] +' + ); + } + + public function testSingleChoiceRequiredWithEmptyValue() + { + $form = $this->factory->createNamed('name', 'choice', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'required' => true, + 'multiple' => false, + 'expanded' => false, + 'empty_value' => 'Test&Me' + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [@required="required"] + [ + ./option[not(@value)][not(@selected)][@disabled][.="[trans]Test&Me[/trans]"] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=3] +' + ); + } + + public function testSingleChoiceRequiredWithEmptyValueViaView() + { + $form = $this->factory->createNamed('name', 'choice', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'required' => true, + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('empty_value' => ''), +'/select + [@name="name"] + [@required="required"] + [ + ./option[not(@value)][not(@selected)][@disabled][.="[trans][/trans]"] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=3] +' + ); + } + + public function testSingleChoiceGrouped() + { + $form = $this->factory->createNamed('name', 'choice', '&a', array( + 'choices' => array( + 'Group&1' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'Group&2' => array('&c' => 'Choice&C'), + ), + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [./optgroup[@label="[trans]Group&1[/trans]"] + [ + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] + ] + [./optgroup[@label="[trans]Group&2[/trans]"] + [./option[@value="&c"][not(@selected)][.="[trans]Choice&C[/trans]"]] + [count(./option)=1] + ] + [count(./optgroup)=2] +' + ); + } + + public function testMultipleChoice() + { + $form = $this->factory->createNamed('name', 'choice', array('&a'), array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'multiple' => true, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name[]"] + [@multiple="multiple"] + [ + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] +' + ); + } + + public function testMultipleChoiceSkipsEmptyValue() + { + $form = $this->factory->createNamed('name', 'choice', array('&a'), array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'multiple' => true, + 'expanded' => false, + 'empty_value' => 'Test&Me' + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name[]"] + [@multiple="multiple"] + [ + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] +' + ); + } + + public function testMultipleChoiceNonRequired() + { + $form = $this->factory->createNamed('name', 'choice', array('&a'), array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'required' => false, + 'multiple' => true, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name[]"] + [@multiple="multiple"] + [ + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] +' + ); + } + + public function testSingleChoiceExpanded() + { + $form = $this->factory->createNamed('name', 'choice', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'multiple' => false, + 'expanded' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] + /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] + /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] + /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(./input)=3] +' + ); + } + + public function testSingleChoiceExpandedWithEmptyValue() + { + $form = $this->factory->createNamed('name', 'choice', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'multiple' => false, + 'expanded' => true, + 'empty_value' => 'Test&Me' + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input[@type="radio"][@name="name"][@id="name_placeholder"][not(@checked)] + /following-sibling::label[@for="name_placeholder"][.="[trans]Test&Me[/trans]"] + /following-sibling::input[@type="radio"][@name="name"][@id="name_0"][@checked] + /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] + /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][not(@checked)] + /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(./input)=4] +' + ); + } + + public function testSingleChoiceExpandedWithBooleanValue() + { + $form = $this->factory->createNamed('name', 'choice', true, array( + 'choices' => array('1' => 'Choice&A', '0' => 'Choice&B'), + 'multiple' => false, + 'expanded' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input[@type="radio"][@name="name"][@id="name_0"][@checked] + /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] + /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][not(@checked)] + /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(./input)=3] +' + ); + } + + public function testMultipleChoiceExpanded() + { + $form = $this->factory->createNamed('name', 'choice', array('&a', '&c'), array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B', '&c' => 'Choice&C'), + 'multiple' => true, + 'expanded' => true, + 'required' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)] + /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] + /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_1"][not(@checked)][not(@required)] + /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] + /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)] + /following-sibling::label[@for="name_2"][.="[trans]Choice&C[/trans]"] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(./input)=4] +' + ); + } + + public function testCountry() + { + $form = $this->factory->createNamed('name', 'country', 'AT'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [./option[@value="AT"][@selected="selected"][.="[trans]Austria[/trans]"]] + [count(./option)>200] +' + ); + } + + public function testCountryWithEmptyValue() + { + $form = $this->factory->createNamed('name', 'country', 'AT', array( + 'empty_value' => 'Select&Country', + 'required' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Select&Country[/trans]"]] + [./option[@value="AT"][@selected="selected"][.="[trans]Austria[/trans]"]] + [count(./option)>201] +' + ); + } + + public function testDateTime() + { + $form = $this->factory->createNamed('name', 'datetime', '2011-02-03 04:05:06', array( + 'input' => 'string', + 'with_seconds' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@id="name_date"] + [ + ./select + [@id="name_date_month"] + [./option[@value="2"][@selected="selected"]] + /following-sibling::select + [@id="name_date_day"] + [./option[@value="3"][@selected="selected"]] + /following-sibling::select + [@id="name_date_year"] + [./option[@value="2011"][@selected="selected"]] + ] + /following-sibling::div + [@id="name_time"] + [ + ./select + [@id="name_time_hour"] + [./option[@value="4"][@selected="selected"]] + /following-sibling::select + [@id="name_time_minute"] + [./option[@value="5"][@selected="selected"]] + ] + ] + [count(.//select)=5] +' + ); + } + + public function testDateTimeWithEmptyValueGlobal() + { + $form = $this->factory->createNamed('name', 'datetime', null, array( + 'input' => 'string', + 'empty_value' => 'Change&Me', + 'required' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@id="name_date"] + [ + ./select + [@id="name_date_month"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + /following-sibling::select + [@id="name_date_day"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + /following-sibling::select + [@id="name_date_year"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + ] + /following-sibling::div + [@id="name_time"] + [ + ./select + [@id="name_time_hour"] + [./option[@value=""][.="[trans]Change&Me[/trans]"]] + /following-sibling::select + [@id="name_time_minute"] + [./option[@value=""][.="[trans]Change&Me[/trans]"]] + ] + ] + [count(.//select)=5] +' + ); + } + + public function testDateTimeWithEmptyValueOnTime() + { + $data = array('year' => '2011', 'month' => '2', 'day' => '3', 'hour' => '', 'minute' => ''); + + $form = $this->factory->createNamed('name', 'datetime', $data, array( + 'input' => 'array', + 'empty_value' => array('hour' => 'Change&Me', 'minute' => 'Change&Me'), + 'required' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@id="name_date"] + [ + ./select + [@id="name_date_month"] + [./option[@value="2"][@selected="selected"]] + /following-sibling::select + [@id="name_date_day"] + [./option[@value="3"][@selected="selected"]] + /following-sibling::select + [@id="name_date_year"] + [./option[@value="2011"][@selected="selected"]] + ] + /following-sibling::div + [@id="name_time"] + [ + ./select + [@id="name_time_hour"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + /following-sibling::select + [@id="name_time_minute"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + ] + ] + [count(.//select)=5] +' + ); + } + + public function testDateTimeWithSeconds() + { + $form = $this->factory->createNamed('name', 'datetime', '2011-02-03 04:05:06', array( + 'input' => 'string', + 'with_seconds' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@id="name_date"] + [ + ./select + [@id="name_date_month"] + [./option[@value="2"][@selected="selected"]] + /following-sibling::select + [@id="name_date_day"] + [./option[@value="3"][@selected="selected"]] + /following-sibling::select + [@id="name_date_year"] + [./option[@value="2011"][@selected="selected"]] + ] + /following-sibling::div + [@id="name_time"] + [ + ./select + [@id="name_time_hour"] + [./option[@value="4"][@selected="selected"]] + /following-sibling::select + [@id="name_time_minute"] + [./option[@value="5"][@selected="selected"]] + /following-sibling::select + [@id="name_time_second"] + [./option[@value="6"][@selected="selected"]] + ] + ] + [count(.//select)=6] +' + ); + } + + public function testDateTimeSingleText() + { + $form = $this->factory->createNamed('name', 'datetime', '2011-02-03 04:05:06', array( + 'input' => 'string', + 'date_widget' => 'single_text', + 'time_widget' => 'single_text', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input + [@type="date"] + [@id="name_date"] + [@name="name[date]"] + [@value="2011-02-03"] + /following-sibling::input + [@type="time"] + [@id="name_time"] + [@name="name[time]"] + [@value="04:05"] + ] +' + ); + } + + public function testDateTimeWithWidgetSingleText() + { + $form = $this->factory->createNamed('name', 'datetime', '2011-02-03 04:05:06', array( + 'input' => 'string', + 'widget' => 'single_text', + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="datetime"] + [@name="name"] + [@value="2011-02-03T04:05:06Z"] +' + ); + } + + public function testDateTimeWithWidgetSingleTextIgnoreDateAndTimeWidgets() + { + $form = $this->factory->createNamed('name', 'datetime', '2011-02-03 04:05:06', array( + 'input' => 'string', + 'date_widget' => 'choice', + 'time_widget' => 'choice', + 'widget' => 'single_text', + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="datetime"] + [@name="name"] + [@value="2011-02-03T04:05:06Z"] +' + ); + } + + public function testDateChoice() + { + $form = $this->factory->createNamed('name', 'date', '2011-02-03', array( + 'input' => 'string', + 'widget' => 'choice', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./select + [@id="name_month"] + [./option[@value="2"][@selected="selected"]] + /following-sibling::select + [@id="name_day"] + [./option[@value="3"][@selected="selected"]] + /following-sibling::select + [@id="name_year"] + [./option[@value="2011"][@selected="selected"]] + ] + [count(./select)=3] +' + ); + } + + public function testDateChoiceWithEmptyValueGlobal() + { + $form = $this->factory->createNamed('name', 'date', null, array( + 'input' => 'string', + 'widget' => 'choice', + 'empty_value' => 'Change&Me', + 'required' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./select + [@id="name_month"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + /following-sibling::select + [@id="name_day"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + /following-sibling::select + [@id="name_year"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + ] + [count(./select)=3] +' + ); + } + + public function testDateChoiceWithEmptyValueOnYear() + { + $form = $this->factory->createNamed('name', 'date', null, array( + 'input' => 'string', + 'widget' => 'choice', + 'required' => false, + 'empty_value' => array('year' => 'Change&Me'), + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./select + [@id="name_month"] + [./option[@value="1"]] + /following-sibling::select + [@id="name_day"] + [./option[@value="1"]] + /following-sibling::select + [@id="name_year"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + ] + [count(./select)=3] +' + ); + } + + public function testDateText() + { + $form = $this->factory->createNamed('name', 'date', '2011-02-03', array( + 'input' => 'string', + 'widget' => 'text', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input + [@id="name_month"] + [@type="text"] + [@value="2"] + /following-sibling::input + [@id="name_day"] + [@type="text"] + [@value="3"] + /following-sibling::input + [@id="name_year"] + [@type="text"] + [@value="2011"] + ] + [count(./input)=3] +' + ); + } + + public function testDateSingleText() + { + $form = $this->factory->createNamed('name', 'date', '2011-02-03', array( + 'input' => 'string', + 'widget' => 'single_text', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="date"] + [@name="name"] + [@value="2011-02-03"] +' + ); + } + + public function testDateErrorBubbling() + { + $form = $this->factory->createNamedBuilder('form', 'form') + ->add('date', 'date') + ->getForm(); + $form->get('date')->addError(new FormError('[trans]Error![/trans]')); + $view = $form->createView(); + + $this->assertEmpty($this->renderErrors($view)); + $this->assertNotEmpty($this->renderErrors($view['date'])); + } + + public function testBirthDay() + { + $form = $this->factory->createNamed('name', 'birthday', '2000-02-03', array( + 'input' => 'string', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./select + [@id="name_month"] + [./option[@value="2"][@selected="selected"]] + /following-sibling::select + [@id="name_day"] + [./option[@value="3"][@selected="selected"]] + /following-sibling::select + [@id="name_year"] + [./option[@value="2000"][@selected="selected"]] + ] + [count(./select)=3] +' + ); + } + + public function testBirthDayWithEmptyValue() + { + $form = $this->factory->createNamed('name', 'birthday', '1950-01-01', array( + 'input' => 'string', + 'empty_value' => '', + 'required' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./select + [@id="name_month"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans][/trans]"]] + [./option[@value="1"][@selected="selected"]] + /following-sibling::select + [@id="name_day"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans][/trans]"]] + [./option[@value="1"][@selected="selected"]] + /following-sibling::select + [@id="name_year"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans][/trans]"]] + [./option[@value="1950"][@selected="selected"]] + ] + [count(./select)=3] +' + ); + } + + public function testEmail() + { + $form = $this->factory->createNamed('name', 'email', 'foo&bar'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="email"] + [@name="name"] + [@value="foo&bar"] + [not(@maxlength)] +' + ); + } + + public function testEmailWithMaxLength() + { + $form = $this->factory->createNamed('name', 'email', 'foo&bar', array( + 'max_length' => 123, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="email"] + [@name="name"] + [@value="foo&bar"] + [@maxlength="123"] +' + ); + } + + public function testFile() + { + $form = $this->factory->createNamed('name', 'file'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="file"] +' + ); + } + + public function testHidden() + { + $form = $this->factory->createNamed('name', 'hidden', 'foo&bar'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="hidden"] + [@name="name"] + [@value="foo&bar"] +' + ); + } + + public function testReadOnly() + { + $form = $this->factory->createNamed('name', 'text', null, array( + 'read_only' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="text"] + [@name="name"] + [@readonly="readonly"] +' + ); + } + + public function testDisabled() + { + $form = $this->factory->createNamed('name', 'text', null, array( + 'disabled' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="text"] + [@name="name"] + [@disabled="disabled"] +' + ); + } + + public function testInteger() + { + $form = $this->factory->createNamed('name', 'integer', 123); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="number"] + [@name="name"] + [@value="123"] +' + ); + } + + public function testLanguage() + { + $form = $this->factory->createNamed('name', 'language', 'de'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [./option[@value="de"][@selected="selected"][.="[trans]German[/trans]"]] + [count(./option)>200] +' + ); + } + + public function testLocale() + { + $form = $this->factory->createNamed('name', 'locale', 'de_AT'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [./option[@value="de_AT"][@selected="selected"][.="[trans]German (Austria)[/trans]"]] + [count(./option)>200] +' + ); + } + + public function testMoney() + { + $form = $this->factory->createNamed('name', 'money', 1234.56, array( + 'currency' => 'EUR', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="text"] + [@name="name"] + [@value="1234.56"] + [contains(.., "€")] +' + ); + } + + public function testNumber() + { + $form = $this->factory->createNamed('name', 'number', 1234.56); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="text"] + [@name="name"] + [@value="1234.56"] +' + ); + } + + public function testPassword() + { + $form = $this->factory->createNamed('name', 'password', 'foo&bar'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="password"] + [@name="name"] +' + ); + } + + public function testPasswordSubmittedWithNotAlwaysEmpty() + { + $form = $this->factory->createNamed('name', 'password', null, array( + 'always_empty' => false, + )); + $form->submit('foo&bar'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="password"] + [@name="name"] + [@value="foo&bar"] +' + ); + } + + public function testPasswordWithMaxLength() + { + $form = $this->factory->createNamed('name', 'password', 'foo&bar', array( + 'max_length' => 123, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="password"] + [@name="name"] + [@maxlength="123"] +' + ); + } + + public function testPercent() + { + $form = $this->factory->createNamed('name', 'percent', 0.1); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="text"] + [@name="name"] + [@value="10"] + [contains(.., "%")] +' + ); + } + + public function testCheckedRadio() + { + $form = $this->factory->createNamed('name', 'radio', true); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="radio"] + [@name="name"] + [@checked="checked"] + [@value="1"] +' + ); + } + + public function testUncheckedRadio() + { + $form = $this->factory->createNamed('name', 'radio', false); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="radio"] + [@name="name"] + [not(@checked)] +' + ); + } + + public function testRadioWithValue() + { + $form = $this->factory->createNamed('name', 'radio', false, array( + 'value' => 'foo&bar', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="radio"] + [@name="name"] + [@value="foo&bar"] +' + ); + } + + public function testTextarea() + { + $form = $this->factory->createNamed('name', 'textarea', 'foo&bar', array( + 'pattern' => 'foo', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/textarea + [@name="name"] + [not(@pattern)] + [.="foo&bar"] +' + ); + } + + public function testText() + { + $form = $this->factory->createNamed('name', 'text', 'foo&bar'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="text"] + [@name="name"] + [@value="foo&bar"] + [not(@maxlength)] +' + ); + } + + public function testTextWithMaxLength() + { + $form = $this->factory->createNamed('name', 'text', 'foo&bar', array( + 'max_length' => 123, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="text"] + [@name="name"] + [@value="foo&bar"] + [@maxlength="123"] +' + ); + } + + public function testSearch() + { + $form = $this->factory->createNamed('name', 'search', 'foo&bar'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="search"] + [@name="name"] + [@value="foo&bar"] + [not(@maxlength)] +' + ); + } + + public function testTime() + { + $form = $this->factory->createNamed('name', 'time', '04:05:06', array( + 'input' => 'string', + 'with_seconds' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./select + [@id="name_hour"] + [not(@size)] + [./option[@value="4"][@selected="selected"]] + /following-sibling::select + [@id="name_minute"] + [not(@size)] + [./option[@value="5"][@selected="selected"]] + ] + [count(./select)=2] +' + ); + } + + public function testTimeWithSeconds() + { + $form = $this->factory->createNamed('name', 'time', '04:05:06', array( + 'input' => 'string', + 'with_seconds' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./select + [@id="name_hour"] + [not(@size)] + [./option[@value="4"][@selected="selected"]] + [count(./option)>23] + /following-sibling::select + [@id="name_minute"] + [not(@size)] + [./option[@value="5"][@selected="selected"]] + [count(./option)>59] + /following-sibling::select + [@id="name_second"] + [not(@size)] + [./option[@value="6"][@selected="selected"]] + [count(./option)>59] + ] + [count(./select)=3] +' + ); + } + + public function testTimeText() + { + $form = $this->factory->createNamed('name', 'time', '04:05:06', array( + 'input' => 'string', + 'widget' => 'text', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input + [@type="text"] + [@id="name_hour"] + [@name="name[hour]"] + [@value="04"] + [@size="1"] + [@required="required"] + /following-sibling::input + [@type="text"] + [@id="name_minute"] + [@name="name[minute]"] + [@value="05"] + [@size="1"] + [@required="required"] + ] + [count(./input)=2] +' + ); + } + + public function testTimeSingleText() + { + $form = $this->factory->createNamed('name', 'time', '04:05:06', array( + 'input' => 'string', + 'widget' => 'single_text', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="time"] + [@name="name"] + [@value="04:05"] + [not(@size)] +' + ); + } + + public function testTimeWithEmptyValueGlobal() + { + $form = $this->factory->createNamed('name', 'time', null, array( + 'input' => 'string', + 'empty_value' => 'Change&Me', + 'required' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./select + [@id="name_hour"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + [count(./option)>24] + /following-sibling::select + [@id="name_minute"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + [count(./option)>60] + ] + [count(./select)=2] +' + ); + } + + public function testTimeWithEmptyValueOnYear() + { + $form = $this->factory->createNamed('name', 'time', null, array( + 'input' => 'string', + 'required' => false, + 'empty_value' => array('hour' => 'Change&Me'), + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./select + [@id="name_hour"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + [count(./option)>24] + /following-sibling::select + [@id="name_minute"] + [./option[@value="1"]] + [count(./option)>59] + ] + [count(./select)=2] +' + ); + } + + public function testTimeErrorBubbling() + { + $form = $this->factory->createNamedBuilder('form', 'form') + ->add('time', 'time') + ->getForm(); + $form->get('time')->addError(new FormError('[trans]Error![/trans]')); + $view = $form->createView(); + + $this->assertEmpty($this->renderErrors($view)); + $this->assertNotEmpty($this->renderErrors($view['time'])); + } + + public function testTimezone() + { + $form = $this->factory->createNamed('name', 'timezone', 'Europe/Vienna'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [@required="required"] + [./optgroup + [@label="[trans]Europe[/trans]"] + [./option[@value="Europe/Vienna"][@selected="selected"][.="[trans]Vienna[/trans]"]] + ] + [count(./optgroup)>10] + [count(.//option)>200] +' + ); + } + + public function testTimezoneWithEmptyValue() + { + $form = $this->factory->createNamed('name', 'timezone', null, array( + 'empty_value' => 'Select&Timezone', + 'required' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Select&Timezone[/trans]"]] + [count(./optgroup)>10] + [count(.//option)>201] +' + ); + } + + public function testUrl() + { + $url = 'http://www.google.com?foo1=bar1&foo2=bar2'; + $form = $this->factory->createNamed('name', 'url', $url); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="url"] + [@name="name"] + [@value="http://www.google.com?foo1=bar1&foo2=bar2"] +' + ); + } + + public function testCollectionPrototype() + { + $form = $this->factory->createNamedBuilder('name', 'form', array('items' => array('one', 'two', 'three'))) + ->add('items', 'collection', array('allow_add' => true)) + ->getForm() + ->createView(); + + $html = $this->renderWidget($form); + + $this->assertMatchesXpath($html, + '//div[@id="name_items"][@data-prototype] + | + //table[@id="name_items"][@data-prototype]' + ); + } + + public function testEmptyRootFormName() + { + $form = $this->factory->createNamedBuilder('', 'form') + ->add('child', 'text') + ->getForm(); + + $this->assertMatchesXpath($this->renderWidget($form->createView()), + '//input[@type="hidden"][@id="_token"][@name="_token"] + | + //input[@type="text"][@id="child"][@name="child"]' + , 2); + } + + public function testButton() + { + $form = $this->factory->createNamed('name', 'button'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), + '/button[@type="button"][@name="name"][.="[trans]Name[/trans]"]' + ); + } + + public function testButtonLabelIsEmpty() + { + $form = $this->factory->createNamed('name', 'button'); + + $this->assertSame('', $this->renderLabel($form->createView())); + } + + public function testSubmit() + { + $form = $this->factory->createNamed('name', 'submit'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), + '/button[@type="submit"][@name="name"]' + ); + } + + public function testReset() + { + $form = $this->factory->createNamed('name', 'reset'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), + '/button[@type="reset"][@name="name"]' + ); + } + + public function testStartTag() + { + $form = $this->factory->create('form', null, array( + 'method' => 'get', + 'action' => 'http://example.com/directory' + )); + + $html = $this->renderStart($form->createView()); + + $this->assertSame('
', $html); + } + + public function testStartTagForPutRequest() + { + $form = $this->factory->create('form', null, array( + 'method' => 'put', + 'action' => 'http://example.com/directory' + )); + + $html = $this->renderStart($form->createView()); + + $this->assertMatchesXpath($html . '', +'/form + [./input[@type="hidden"][@name="_method"][@value="PUT"]] + [@method="post"] + [@action="http://example.com/directory"]' + ); + } + + public function testStartTagWithOverriddenVars() + { + $form = $this->factory->create('form', null, array( + 'method' => 'put', + 'action' => 'http://example.com/directory', + )); + + $html = $this->renderStart($form->createView(), array( + 'method' => 'post', + 'action' => 'http://foo.com/directory' + )); + + $this->assertSame('
', $html); + } + + public function testStartTagForMultipartForm() + { + $form = $this->factory->createBuilder('form', null, array( + 'method' => 'get', + 'action' => 'http://example.com/directory' + )) + ->add('file', 'file') + ->getForm(); + + $html = $this->renderStart($form->createView()); + + $this->assertSame('', $html); + } + + public function testStartTagWithExtraAttributes() + { + $form = $this->factory->create('form', null, array( + 'method' => 'get', + 'action' => 'http://example.com/directory' + )); + + $html = $this->renderStart($form->createView(), array( + 'attr' => array('class' => 'foobar'), + )); + + $this->assertSame('', $html); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/AbstractRequestHandlerTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/AbstractRequestHandlerTest.php new file mode 100644 index 00000000..cef8f3bf --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/AbstractRequestHandlerTest.php @@ -0,0 +1,280 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +/** + * @author Bernhard Schussek + */ +abstract class AbstractRequestHandlerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Symfony\Component\Form\RequestHandlerInterface + */ + protected $requestHandler; + + protected $request; + + protected function setUp() + { + $this->requestHandler = $this->getRequestHandler(); + $this->request = null; + } + + public function methodExceptGetProvider() + { + return array( + array('POST'), + array('PUT'), + array('DELETE'), + array('PATCH'), + ); + } + + public function methodProvider() + { + return array_merge(array( + array('GET'), + ), $this->methodExceptGetProvider()); + } + + /** + * @dataProvider methodProvider + */ + public function testSubmitIfNameInRequest($method) + { + $form = $this->getMockForm('param1', $method); + + $this->setRequestData($method, array( + 'param1' => 'DATA', + )); + + $form->expects($this->once()) + ->method('submit') + ->with('DATA', 'PATCH' !== $method); + + $this->requestHandler->handleRequest($form, $this->request); + } + + /** + * @dataProvider methodProvider + */ + public function testDoNotSubmitIfWrongRequestMethod($method) + { + $form = $this->getMockForm('param1', $method); + + $otherMethod = 'POST' === $method ? 'PUT' : 'POST'; + + $this->setRequestData($otherMethod, array( + 'param1' => 'DATA', + )); + + $form->expects($this->never()) + ->method('submit'); + + $this->requestHandler->handleRequest($form, $this->request); + } + + /** + * @dataProvider methodExceptGetProvider + */ + public function testSubmitSimpleFormWithNullIfNameNotInRequestAndNotGetRequest($method) + { + $form = $this->getMockForm('param1', $method, false); + + $this->setRequestData($method, array( + 'paramx' => array(), + )); + + $form->expects($this->once()) + ->method('submit') + ->with($this->identicalTo(null), 'PATCH' !== $method); + + $this->requestHandler->handleRequest($form, $this->request); + } + + /** + * @dataProvider methodExceptGetProvider + */ + public function testSubmitCompoundFormWithArrayIfNameNotInRequestAndNotGetRequest($method) + { + $form = $this->getMockForm('param1', $method, true); + + $this->setRequestData($method, array( + 'paramx' => array(), + )); + + $form->expects($this->once()) + ->method('submit') + ->with($this->identicalTo(array()), 'PATCH' !== $method); + + $this->requestHandler->handleRequest($form, $this->request); + } + + public function testDoNotSubmitIfNameNotInRequestAndGetRequest() + { + $form = $this->getMockForm('param1', 'GET'); + + $this->setRequestData('GET', array( + 'paramx' => array(), + )); + + $form->expects($this->never()) + ->method('submit'); + + $this->requestHandler->handleRequest($form, $this->request); + } + + /** + * @dataProvider methodProvider + */ + public function testSubmitFormWithEmptyNameIfAtLeastOneFieldInRequest($method) + { + $form = $this->getMockForm('', $method); + $form->expects($this->any()) + ->method('all') + ->will($this->returnValue(array( + 'param1' => $this->getMockForm('param1'), + 'param2' => $this->getMockForm('param2'), + ))); + + $this->setRequestData($method, $requestData = array( + 'param1' => 'submitted value', + 'paramx' => 'submitted value', + )); + + $form->expects($this->once()) + ->method('submit') + ->with($requestData, 'PATCH' !== $method); + + $this->requestHandler->handleRequest($form, $this->request); + } + + /** + * @dataProvider methodProvider + */ + public function testDoNotSubmitFormWithEmptyNameIfNoFieldInRequest($method) + { + $form = $this->getMockForm('', $method); + $form->expects($this->any()) + ->method('all') + ->will($this->returnValue(array( + 'param1' => $this->getMockForm('param1'), + 'param2' => $this->getMockForm('param2'), + ))); + + $this->setRequestData($method, array( + 'paramx' => 'submitted value', + )); + + $form->expects($this->never()) + ->method('submit'); + + $this->requestHandler->handleRequest($form, $this->request); + } + + /** + * @dataProvider methodExceptGetProvider + */ + public function testMergeParamsAndFiles($method) + { + $form = $this->getMockForm('param1', $method); + $file = $this->getMockFile(); + + $this->setRequestData($method, array( + 'param1' => array( + 'field1' => 'DATA', + ), + ), array( + 'param1' => array( + 'field2' => $file, + ), + )); + + $form->expects($this->once()) + ->method('submit') + ->with(array( + 'field1' => 'DATA', + 'field2' => $file, + ), 'PATCH' !== $method); + + $this->requestHandler->handleRequest($form, $this->request); + } + + /** + * @dataProvider methodExceptGetProvider + */ + public function testParamTakesPrecedenceOverFile($method) + { + $form = $this->getMockForm('param1', $method); + $file = $this->getMockFile(); + + $this->setRequestData($method, array( + 'param1' => 'DATA', + ), array( + 'param1' => $file, + )); + + $form->expects($this->once()) + ->method('submit') + ->with('DATA', 'PATCH' !== $method); + + $this->requestHandler->handleRequest($form, $this->request); + } + + /** + * @dataProvider methodExceptGetProvider + */ + public function testSubmitFileIfNoParam($method) + { + $form = $this->getMockForm('param1', $method); + $file = $this->getMockFile(); + + $this->setRequestData($method, array( + 'param1' => null, + ), array( + 'param1' => $file, + )); + + $form->expects($this->once()) + ->method('submit') + ->with($file, 'PATCH' !== $method); + + $this->requestHandler->handleRequest($form, $this->request); + } + + abstract protected function setRequestData($method, $data, $files = array()); + + abstract protected function getRequestHandler(); + + abstract protected function getMockFile(); + + protected function getMockForm($name, $method = null, $compound = true) + { + $config = $this->getMock('Symfony\Component\Form\FormConfigInterface'); + $config->expects($this->any()) + ->method('getMethod') + ->will($this->returnValue($method)); + $config->expects($this->any()) + ->method('getCompound') + ->will($this->returnValue($compound)); + + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $form->expects($this->any()) + ->method('getName') + ->will($this->returnValue($name)); + $form->expects($this->any()) + ->method('getConfig') + ->will($this->returnValue($config)); + + return $form; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php new file mode 100644 index 00000000..5c911951 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php @@ -0,0 +1,509 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\FormError; + +abstract class AbstractTableLayoutTest extends AbstractLayoutTest +{ + public function testRow() + { + $form = $this->factory->createNamed('name', 'text'); + $form->addError(new FormError('[trans]Error![/trans]')); + $view = $form->createView(); + $html = $this->renderRow($view); + + $this->assertMatchesXpath($html, +'/tr + [ + ./td + [./label[@for="name"]] + /following-sibling::td + [ + ./ul + [./li[.="[trans]Error![/trans]"]] + [count(./li)=1] + /following-sibling::input[@id="name"] + ] + ] +' + ); + } + + public function testLabelIsNotRenderedWhenSetToFalse() + { + $form = $this->factory->createNamed('name', 'text', null, array( + 'label' => false + )); + $html = $this->renderRow($form->createView()); + + $this->assertMatchesXpath($html, +'/tr + [ + ./td + [count(//label)=0] + /following-sibling::td + [./input[@id="name"]] + ] +' + ); + } + + public function testRepeatedRow() + { + $form = $this->factory->createNamed('name', 'repeated'); + $html = $this->renderRow($form->createView()); + + $this->assertMatchesXpath($html, +'/tr + [ + ./td + [./label[@for="name_first"]] + /following-sibling::td + [./input[@id="name_first"]] + ] +/following-sibling::tr + [ + ./td + [./label[@for="name_second"]] + /following-sibling::td + [./input[@id="name_second"]] + ] +/following-sibling::tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] + [count(../tr)=3] +' + ); + } + + public function testRepeatedRowWithErrors() + { + $form = $this->factory->createNamed('name', 'repeated'); + $form->addError(new FormError('[trans]Error![/trans]')); + $view = $form->createView(); + $html = $this->renderRow($view); + + // The errors of the form are not rendered by intention! + // In practice, repeated fields cannot have errors as all errors + // on them are mapped to the first child. + // (see RepeatedTypeValidatorExtension) + + $this->assertMatchesXpath($html, +'/tr + [ + ./td + [./label[@for="name_first"]] + /following-sibling::td + [./input[@id="name_first"]] + ] +/following-sibling::tr + [ + ./td + [./label[@for="name_second"]] + /following-sibling::td + [./input[@id="name_second"]] + ] +/following-sibling::tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] + [count(../tr)=3] +' + ); + } + + public function testButtonRow() + { + $form = $this->factory->createNamed('name', 'button'); + $view = $form->createView(); + $html = $this->renderRow($view); + + $this->assertMatchesXpath($html, +'/tr + [ + ./td + [.=""] + /following-sibling::td + [./button[@type="button"][@name="name"]] + ] + [count(//label)=0] +' + ); + } + + public function testRest() + { + $view = $this->factory->createNamedBuilder('name', 'form') + ->add('field1', 'text') + ->add('field2', 'repeated') + ->add('field3', 'text') + ->add('field4', 'text') + ->getForm() + ->createView(); + + // Render field2 row -> does not implicitly call renderWidget because + // it is a repeated field! + $this->renderRow($view['field2']); + + // Render field3 widget + $this->renderWidget($view['field3']); + + // Rest should only contain field1 and field4 + $html = $this->renderRest($view); + + $this->assertMatchesXpath($html, +'/tr + [ + ./td + [./label[@for="name_field1"]] + /following-sibling::td + [./input[@id="name_field1"]] + ] +/following-sibling::tr + [ + ./td + [./label[@for="name_field4"]] + /following-sibling::td + [./input[@id="name_field4"]] + ] + [count(../tr)=3] + [count(..//label)=2] + [count(..//input)=3] +/following-sibling::tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] +' + ); + } + + public function testCollection() + { + $form = $this->factory->createNamed('name', 'collection', array('a', 'b'), array( + 'type' => 'text', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/table + [ + ./tr[./td/input[@type="text"][@value="a"]] + /following-sibling::tr[./td/input[@type="text"][@value="b"]] + /following-sibling::tr[@style="display: none"][./td[@colspan="2"]/input[@type="hidden"][@id="name__token"]] + ] + [count(./tr[./td/input])=3] +' + ); + } + + public function testEmptyCollection() + { + $form = $this->factory->createNamed('name', 'collection', array(), array( + 'type' => 'text', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/table + [./tr[@style="display: none"][./td[@colspan="2"]/input[@type="hidden"][@id="name__token"]]] + [count(./tr[./td/input])=1] +' + ); + } + + public function testForm() + { + $view = $this->factory->createNamedBuilder('name', 'form') + ->setMethod('PUT') + ->setAction('http://example.com') + ->add('firstName', 'text') + ->add('lastName', 'text') + ->getForm() + ->createView(); + + $html = $this->renderForm($view, array( + 'id' => 'my&id', + 'attr' => array('class' => 'my&class'), + )); + + $this->assertMatchesXpath($html, +'/form + [ + ./input[@type="hidden"][@name="_method"][@value="PUT"] + /following-sibling::table + [ + ./tr + [ + ./td + [./label[@for="name_firstName"]] + /following-sibling::td + [./input[@id="name_firstName"]] + ] + /following-sibling::tr + [ + ./td + [./label[@for="name_lastName"]] + /following-sibling::td + [./input[@id="name_lastName"]] + ] + /following-sibling::tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] + ] + [count(.//input)=3] + [@id="my&id"] + [@class="my&class"] + ] + [@method="post"] + [@action="http://example.com"] + [@class="my&class"] +' + ); + } + + public function testFormWidget() + { + $view = $this->factory->createNamedBuilder('name', 'form') + ->add('firstName', 'text') + ->add('lastName', 'text') + ->getForm() + ->createView(); + + $this->assertWidgetMatchesXpath($view, array(), +'/table + [ + ./tr + [ + ./td + [./label[@for="name_firstName"]] + /following-sibling::td + [./input[@id="name_firstName"]] + ] + /following-sibling::tr + [ + ./td + [./label[@for="name_lastName"]] + /following-sibling::td + [./input[@id="name_lastName"]] + ] + /following-sibling::tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] + ] + [count(.//input)=3] +' + ); + } + + // https://github.com/symfony/symfony/issues/2308 + public function testNestedFormError() + { + $form = $this->factory->createNamedBuilder('name', 'form') + ->add($this->factory + ->createNamedBuilder('child', 'form', null, array('error_bubbling' => false)) + ->add('grandChild', 'form') + ) + ->getForm(); + + $form->get('child')->addError(new FormError('[trans]Error![/trans]')); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/table + [ + ./tr/td/ul[./li[.="[trans]Error![/trans]"]] + /following-sibling::table[@id="name_child"] + ] + [count(.//li[.="[trans]Error![/trans]"])=1] +' + ); + } + + public function testCsrf() + { + $this->csrfProvider->expects($this->any()) + ->method('generateCsrfToken') + ->will($this->returnValue('foo&bar')); + + $form = $this->factory->createNamedBuilder('name', 'form') + ->add($this->factory + // No CSRF protection on nested forms + ->createNamedBuilder('child', 'form') + ->add($this->factory->createNamedBuilder('grandchild', 'text')) + ) + ->getForm(); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/table + [ + ./tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] + ] + [count(.//input[@type="hidden"])=1] +' + ); + } + + public function testRepeated() + { + $form = $this->factory->createNamed('name', 'repeated', 'foobar', array( + 'type' => 'text', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/table + [ + ./tr + [ + ./td + [./label[@for="name_first"]] + /following-sibling::td + [./input[@type="text"][@id="name_first"]] + ] + /following-sibling::tr + [ + ./td + [./label[@for="name_second"]] + /following-sibling::td + [./input[@type="text"][@id="name_second"]] + ] + /following-sibling::tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] + ] + [count(.//input)=3] +' + ); + } + + public function testRepeatedWithCustomOptions() + { + $form = $this->factory->createNamed('name', 'repeated', 'foobar', array( + 'type' => 'password', + 'first_options' => array('label' => 'Test', 'required' => false), + 'second_options' => array('label' => 'Test2') + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/table + [ + ./tr + [ + ./td + [./label[@for="name_first"][.="[trans]Test[/trans]"]] + /following-sibling::td + [./input[@type="password"][@id="name_first"][@required="required"]] + ] + /following-sibling::tr + [ + ./td + [./label[@for="name_second"][.="[trans]Test2[/trans]"]] + /following-sibling::td + [./input[@type="password"][@id="name_second"][@required="required"]] + ] + /following-sibling::tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] + ] + [count(.//input)=3] +' + ); + } + + /** + * The block "_name_child_label" should be overridden in the theme of the + * implemented driver. + */ + public function testCollectionRowWithCustomBlock() + { + $collection = array('one', 'two', 'three'); + $form = $this->factory->createNamedBuilder('name', 'collection', $collection) + ->getForm(); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/table + [ + ./tr[./td/label[.="Custom label: [trans]0[/trans]"]] + /following-sibling::tr[./td/label[.="Custom label: [trans]1[/trans]"]] + /following-sibling::tr[./td/label[.="Custom label: [trans]2[/trans]"]] + ] +' + ); + } + + public function testFormEndWithRest() + { + $view = $this->factory->createNamedBuilder('name', 'form') + ->add('field1', 'text') + ->add('field2', 'text') + ->getForm() + ->createView(); + + $this->renderWidget($view['field1']); + + // Rest should only contain field2 + $html = $this->renderEnd($view); + + // Insert the start tag, the end tag should be rendered by the helper + // Unfortunately this is not valid HTML, because the surrounding table + // tag is missing. If someone renders a form with table layout + // manually, she should call form_rest() explicitly within the
+ // tag. + $this->assertMatchesXpath('' . $html, +'/form + [ + ./tr + [ + ./td + [./label[@for="name_field2"]] + /following-sibling::td + [./input[@id="name_field2"]] + ] + /following-sibling::tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] + ] +' + ); + } + + public function testFormEndWithoutRest() + { + $view = $this->factory->createNamedBuilder('name', 'form') + ->add('field1', 'text') + ->add('field2', 'text') + ->getForm() + ->createView(); + + $this->renderWidget($view['field1']); + + // Rest should only contain field2, but isn't rendered + $html = $this->renderEnd($view, array('render_rest' => false)); + + $this->assertEquals('', $html); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/CompoundFormPerformanceTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/CompoundFormPerformanceTest.php new file mode 100644 index 00000000..73c602c5 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/CompoundFormPerformanceTest.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +/** + * @author Bernhard Schussek + */ +class CompoundFormPerformanceTest extends \Symfony\Component\Form\Tests\FormPerformanceTestCase +{ + /** + * Create a compound form multiple times, as happens in a collection form + * + * @group benchmark + */ + public function testArrayBasedForm() + { + $this->setMaxRunningTime(1); + + for ($i = 0; $i < 40; ++$i) { + $form = $this->factory->createBuilder('form') + ->add('firstName', 'text') + ->add('lastName', 'text') + ->add('gender', 'choice', array( + 'choices' => array('male' => 'Male', 'female' => 'Female'), + 'required' => false, + )) + ->add('age', 'number') + ->add('birthDate', 'birthday') + ->add('city', 'choice', array( + // simulate 300 different cities + 'choices' => range(1, 300), + )) + ->getForm(); + + // load the form into a view + $form->createView(); + } + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/CompoundFormTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/CompoundFormTest.php new file mode 100644 index 00000000..b240d2d0 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/CompoundFormTest.php @@ -0,0 +1,759 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationRequestHandler; +use Symfony\Component\Form\FormError; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\File\UploadedFile; +use Symfony\Component\Form\Tests\Fixtures\FixedDataTransformer; + +class CompoundFormTest extends AbstractFormTest +{ + public function testValidIfAllChildrenAreValid() + { + $this->form->add($this->getValidForm('firstName')); + $this->form->add($this->getValidForm('lastName')); + + $this->form->submit(array( + 'firstName' => 'Bernhard', + 'lastName' => 'Schussek', + )); + + $this->assertTrue($this->form->isValid()); + } + + public function testInvalidIfChildIsInvalid() + { + $this->form->add($this->getValidForm('firstName')); + $this->form->add($this->getInvalidForm('lastName')); + + $this->form->submit(array( + 'firstName' => 'Bernhard', + 'lastName' => 'Schussek', + )); + + $this->assertFalse($this->form->isValid()); + } + + public function testSubmitForwardsNullIfValueIsMissing() + { + $child = $this->getMockForm('firstName'); + + $this->form->add($child); + + $child->expects($this->once()) + ->method('submit') + ->with($this->equalTo(null)); + + $this->form->submit(array()); + } + + public function testSubmitDoesNotForwardNullIfNotClearMissing() + { + $child = $this->getMockForm('firstName'); + + $this->form->add($child); + + $child->expects($this->never()) + ->method('submit'); + + $this->form->submit(array(), false); + } + + public function testClearMissingFlagIsForwarded() + { + $child = $this->getMockForm('firstName'); + + $this->form->add($child); + + $child->expects($this->once()) + ->method('submit') + ->with($this->equalTo('foo'), false); + + $this->form->submit(array('firstName' => 'foo'), false); + } + + public function testCloneChildren() + { + $child = $this->getBuilder('child')->getForm(); + $this->form->add($child); + + $clone = clone $this->form; + + $this->assertNotSame($this->form, $clone); + $this->assertNotSame($child, $clone['child']); + } + + public function testNotEmptyIfChildNotEmpty() + { + $child = $this->getMockForm(); + $child->expects($this->once()) + ->method('isEmpty') + ->will($this->returnValue(false)); + + $this->form->setData(null); + $this->form->add($child); + + $this->assertFalse($this->form->isEmpty()); + } + + public function testValidIfSubmittedAndDisabledWithChildren() + { + $this->factory->expects($this->once()) + ->method('createNamedBuilder') + ->with('name', 'text', null, array()) + ->will($this->returnValue($this->getBuilder('name'))); + + $form = $this->getBuilder('person') + ->setDisabled(true) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->add('name', 'text') + ->getForm(); + $form->submit(array('name' => 'Jacques Doe')); + + $this->assertTrue($form->isValid()); + } + + public function testNotValidIfChildNotValid() + { + $child = $this->getMockForm(); + $child->expects($this->once()) + ->method('isValid') + ->will($this->returnValue(false)); + + $this->form->add($child); + $this->form->submit(array()); + + $this->assertFalse($this->form->isValid()); + } + + public function testAdd() + { + $child = $this->getBuilder('foo')->getForm(); + $this->form->add($child); + + $this->assertTrue($this->form->has('foo')); + $this->assertSame($this->form, $child->getParent()); + $this->assertSame(array('foo' => $child), $this->form->all()); + } + + public function testAddUsingNameAndType() + { + $child = $this->getBuilder('foo')->getForm(); + + $this->factory->expects($this->once()) + ->method('createNamed') + ->with('foo', 'text', null, array( + 'bar' => 'baz', + 'auto_initialize' => false, + )) + ->will($this->returnValue($child)); + + $this->form->add('foo', 'text', array('bar' => 'baz')); + + $this->assertTrue($this->form->has('foo')); + $this->assertSame($this->form, $child->getParent()); + $this->assertSame(array('foo' => $child), $this->form->all()); + } + + public function testAddUsingIntegerNameAndType() + { + $child = $this->getBuilder(0)->getForm(); + + $this->factory->expects($this->once()) + ->method('createNamed') + ->with('0', 'text', null, array( + 'bar' => 'baz', + 'auto_initialize' => false, + )) + ->will($this->returnValue($child)); + + // in order to make casting unnecessary + $this->form->add(0, 'text', array('bar' => 'baz')); + + $this->assertTrue($this->form->has(0)); + $this->assertSame($this->form, $child->getParent()); + $this->assertSame(array(0 => $child), $this->form->all()); + } + + public function testAddUsingNameButNoType() + { + $this->form = $this->getBuilder('name', null, '\stdClass') + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + + $child = $this->getBuilder('foo')->getForm(); + + $this->factory->expects($this->once()) + ->method('createForProperty') + ->with('\stdClass', 'foo') + ->will($this->returnValue($child)); + + $this->form->add('foo'); + + $this->assertTrue($this->form->has('foo')); + $this->assertSame($this->form, $child->getParent()); + $this->assertSame(array('foo' => $child), $this->form->all()); + } + + public function testAddUsingNameButNoTypeAndOptions() + { + $this->form = $this->getBuilder('name', null, '\stdClass') + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + + $child = $this->getBuilder('foo')->getForm(); + + $this->factory->expects($this->once()) + ->method('createForProperty') + ->with('\stdClass', 'foo', null, array( + 'bar' => 'baz', + 'auto_initialize' => false, + )) + ->will($this->returnValue($child)); + + $this->form->add('foo', null, array('bar' => 'baz')); + + $this->assertTrue($this->form->has('foo')); + $this->assertSame($this->form, $child->getParent()); + $this->assertSame(array('foo' => $child), $this->form->all()); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\AlreadySubmittedException + */ + public function testAddThrowsExceptionIfAlreadySubmitted() + { + $this->form->submit(array()); + $this->form->add($this->getBuilder('foo')->getForm()); + } + + public function testRemove() + { + $child = $this->getBuilder('foo')->getForm(); + $this->form->add($child); + $this->form->remove('foo'); + + $this->assertNull($child->getParent()); + $this->assertCount(0, $this->form); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\AlreadySubmittedException + */ + public function testRemoveThrowsExceptionIfAlreadySubmitted() + { + $this->form->add($this->getBuilder('foo')->setCompound(false)->getForm()); + $this->form->submit(array('foo' => 'bar')); + $this->form->remove('foo'); + } + + public function testRemoveIgnoresUnknownName() + { + $this->form->remove('notexisting'); + } + + public function testArrayAccess() + { + $child = $this->getBuilder('foo')->getForm(); + + $this->form[] = $child; + + $this->assertTrue(isset($this->form['foo'])); + $this->assertSame($child, $this->form['foo']); + + unset($this->form['foo']); + + $this->assertFalse(isset($this->form['foo'])); + } + + public function testCountable() + { + $this->form->add($this->getBuilder('foo')->getForm()); + $this->form->add($this->getBuilder('bar')->getForm()); + + $this->assertCount(2, $this->form); + } + + public function testIterator() + { + $this->form->add($this->getBuilder('foo')->getForm()); + $this->form->add($this->getBuilder('bar')->getForm()); + + $this->assertSame($this->form->all(), iterator_to_array($this->form)); + } + + public function testAddMapsViewDataToFormIfInitialized() + { + $test = $this; + $mapper = $this->getDataMapper(); + $form = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($mapper) + ->addViewTransformer(new FixedDataTransformer(array( + '' => '', + 'foo' => 'bar', + ))) + ->setData('foo') + ->getForm(); + + $child = $this->getBuilder()->getForm(); + $mapper->expects($this->once()) + ->method('mapDataToForms') + ->with('bar', $this->isInstanceOf('\RecursiveIteratorIterator')) + ->will($this->returnCallback(function ($data, \RecursiveIteratorIterator $iterator) use ($child, $test) { + $test->assertInstanceOf('Symfony\Component\Form\Util\InheritDataAwareIterator', $iterator->getInnerIterator()); + $test->assertSame(array($child), iterator_to_array($iterator)); + })); + + $form->initialize(); + $form->add($child); + } + + public function testAddDoesNotMapViewDataToFormIfNotInitialized() + { + $mapper = $this->getDataMapper(); + $form = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($mapper) + ->getForm(); + + $child = $this->getBuilder()->getForm(); + $mapper->expects($this->never()) + ->method('mapDataToForms'); + + $form->add($child); + } + + public function testAddDoesNotMapViewDataToFormIfInheritData() + { + $mapper = $this->getDataMapper(); + $form = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($mapper) + ->setInheritData(true) + ->getForm(); + + $child = $this->getBuilder()->getForm(); + $mapper->expects($this->never()) + ->method('mapDataToForms'); + + $form->initialize(); + $form->add($child); + } + + public function testSetDataMapsViewDataToChildren() + { + $test = $this; + $mapper = $this->getDataMapper(); + $form = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($mapper) + ->addViewTransformer(new FixedDataTransformer(array( + '' => '', + 'foo' => 'bar', + ))) + ->getForm(); + + $form->add($child1 = $this->getBuilder('firstName')->getForm()); + $form->add($child2 = $this->getBuilder('lastName')->getForm()); + + $mapper->expects($this->once()) + ->method('mapDataToForms') + ->with('bar', $this->isInstanceOf('\RecursiveIteratorIterator')) + ->will($this->returnCallback(function ($data, \RecursiveIteratorIterator $iterator) use ($child1, $child2, $test) { + $test->assertInstanceOf('Symfony\Component\Form\Util\InheritDataAwareIterator', $iterator->getInnerIterator()); + $test->assertSame(array('firstName' => $child1, 'lastName' => $child2), iterator_to_array($iterator)); + })); + + $form->setData('foo'); + } + + public function testSubmitMapsSubmittedChildrenOntoExistingViewData() + { + $test = $this; + $mapper = $this->getDataMapper(); + $form = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($mapper) + ->addViewTransformer(new FixedDataTransformer(array( + '' => '', + 'foo' => 'bar', + ))) + ->setData('foo') + ->getForm(); + + $form->add($child1 = $this->getBuilder('firstName')->setCompound(false)->getForm()); + $form->add($child2 = $this->getBuilder('lastName')->setCompound(false)->getForm()); + + $mapper->expects($this->once()) + ->method('mapFormsToData') + ->with($this->isInstanceOf('\RecursiveIteratorIterator'), 'bar') + ->will($this->returnCallback(function (\RecursiveIteratorIterator $iterator) use ($child1, $child2, $test) { + $test->assertInstanceOf('Symfony\Component\Form\Util\InheritDataAwareIterator', $iterator->getInnerIterator()); + $test->assertSame(array('firstName' => $child1, 'lastName' => $child2), iterator_to_array($iterator)); + $test->assertEquals('Bernhard', $child1->getData()); + $test->assertEquals('Schussek', $child2->getData()); + })); + + $form->submit(array( + 'firstName' => 'Bernhard', + 'lastName' => 'Schussek', + )); + } + + public function testMapFormsToDataIsNotInvokedIfInheritData() + { + $mapper = $this->getDataMapper(); + $form = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($mapper) + ->setInheritData(true) + ->addViewTransformer(new FixedDataTransformer(array( + '' => '', + 'foo' => 'bar', + ))) + ->getForm(); + + $form->add($child1 = $this->getBuilder('firstName')->setCompound(false)->getForm()); + $form->add($child2 = $this->getBuilder('lastName')->setCompound(false)->getForm()); + + $mapper->expects($this->never()) + ->method('mapFormsToData'); + + $form->submit(array( + 'firstName' => 'Bernhard', + 'lastName' => 'Schussek', + )); + } + + /* + * https://github.com/symfony/symfony/issues/4480 + */ + public function testSubmitRestoresViewDataIfCompoundAndEmpty() + { + $mapper = $this->getDataMapper(); + $object = new \stdClass(); + $form = $this->getBuilder('name', null, 'stdClass') + ->setCompound(true) + ->setDataMapper($mapper) + ->setData($object) + ->getForm(); + + $form->submit(array()); + + $this->assertSame($object, $form->getData()); + } + + public function testSubmitMapsSubmittedChildrenOntoEmptyData() + { + $test = $this; + $mapper = $this->getDataMapper(); + $object = new \stdClass(); + $form = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($mapper) + ->setEmptyData($object) + ->setData(null) + ->getForm(); + + $form->add($child = $this->getBuilder('name')->setCompound(false)->getForm()); + + $mapper->expects($this->once()) + ->method('mapFormsToData') + ->with($this->isInstanceOf('\RecursiveIteratorIterator'), $object) + ->will($this->returnCallback(function (\RecursiveIteratorIterator $iterator) use ($child, $test) { + $test->assertInstanceOf('Symfony\Component\Form\Util\InheritDataAwareIterator', $iterator->getInnerIterator()); + $test->assertSame(array('name' => $child), iterator_to_array($iterator)); + })); + + $form->submit(array( + 'name' => 'Bernhard', + )); + } + + public function requestMethodProvider() + { + return array( + array('POST'), + array('PUT'), + array('DELETE'), + array('PATCH'), + ); + } + + /** + * @dataProvider requestMethodProvider + */ + public function testSubmitPostOrPutRequest($method) + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $path = tempnam(sys_get_temp_dir(), 'sf2'); + touch($path); + + $values = array( + 'author' => array( + 'name' => 'Bernhard', + 'image' => array('filename' => 'foobar.png'), + ), + ); + + $files = array( + 'author' => array( + 'error' => array('image' => UPLOAD_ERR_OK), + 'name' => array('image' => 'upload.png'), + 'size' => array('image' => 123), + 'tmp_name' => array('image' => $path), + 'type' => array('image' => 'image/png'), + ), + ); + + $request = new Request(array(), $values, array(), array(), $files, array( + 'REQUEST_METHOD' => $method, + )); + + $form = $this->getBuilder('author') + ->setMethod($method) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->setRequestHandler(new HttpFoundationRequestHandler()) + ->getForm(); + $form->add($this->getBuilder('name')->getForm()); + $form->add($this->getBuilder('image')->getForm()); + + $form->handleRequest($request); + + $file = new UploadedFile($path, 'upload.png', 'image/png', 123, UPLOAD_ERR_OK); + + $this->assertEquals('Bernhard', $form['name']->getData()); + $this->assertEquals($file, $form['image']->getData()); + + unlink($path); + } + + /** + * @dataProvider requestMethodProvider + */ + public function testSubmitPostOrPutRequestWithEmptyRootFormName($method) + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $path = tempnam(sys_get_temp_dir(), 'sf2'); + touch($path); + + $values = array( + 'name' => 'Bernhard', + 'extra' => 'data', + ); + + $files = array( + 'image' => array( + 'error' => UPLOAD_ERR_OK, + 'name' => 'upload.png', + 'size' => 123, + 'tmp_name' => $path, + 'type' => 'image/png', + ), + ); + + $request = new Request(array(), $values, array(), array(), $files, array( + 'REQUEST_METHOD' => $method, + )); + + $form = $this->getBuilder('') + ->setMethod($method) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->setRequestHandler(new HttpFoundationRequestHandler()) + ->getForm(); + $form->add($this->getBuilder('name')->getForm()); + $form->add($this->getBuilder('image')->getForm()); + + $form->handleRequest($request); + + $file = new UploadedFile($path, 'upload.png', 'image/png', 123, UPLOAD_ERR_OK); + + $this->assertEquals('Bernhard', $form['name']->getData()); + $this->assertEquals($file, $form['image']->getData()); + $this->assertEquals(array('extra' => 'data'), $form->getExtraData()); + + unlink($path); + } + + /** + * @dataProvider requestMethodProvider + */ + public function testSubmitPostOrPutRequestWithSingleChildForm($method) + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $path = tempnam(sys_get_temp_dir(), 'sf2'); + touch($path); + + $files = array( + 'image' => array( + 'error' => UPLOAD_ERR_OK, + 'name' => 'upload.png', + 'size' => 123, + 'tmp_name' => $path, + 'type' => 'image/png', + ), + ); + + $request = new Request(array(), array(), array(), array(), $files, array( + 'REQUEST_METHOD' => $method, + )); + + $form = $this->getBuilder('image') + ->setMethod($method) + ->setRequestHandler(new HttpFoundationRequestHandler()) + ->getForm(); + + $form->handleRequest($request); + + $file = new UploadedFile($path, 'upload.png', 'image/png', 123, UPLOAD_ERR_OK); + + $this->assertEquals($file, $form->getData()); + + unlink($path); + } + + /** + * @dataProvider requestMethodProvider + */ + public function testSubmitPostOrPutRequestWithSingleChildFormUploadedFile($method) + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $path = tempnam(sys_get_temp_dir(), 'sf2'); + touch($path); + + $values = array( + 'name' => 'Bernhard', + ); + + $request = new Request(array(), $values, array(), array(), array(), array( + 'REQUEST_METHOD' => $method, + )); + + $form = $this->getBuilder('name') + ->setMethod($method) + ->setRequestHandler(new HttpFoundationRequestHandler()) + ->getForm(); + + $form->handleRequest($request); + + $this->assertEquals('Bernhard', $form->getData()); + + unlink($path); + } + + public function testSubmitGetRequest() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $values = array( + 'author' => array( + 'firstName' => 'Bernhard', + 'lastName' => 'Schussek', + ), + ); + + $request = new Request($values, array(), array(), array(), array(), array( + 'REQUEST_METHOD' => 'GET', + )); + + $form = $this->getBuilder('author') + ->setMethod('GET') + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->setRequestHandler(new HttpFoundationRequestHandler()) + ->getForm(); + $form->add($this->getBuilder('firstName')->getForm()); + $form->add($this->getBuilder('lastName')->getForm()); + + $form->handleRequest($request); + + $this->assertEquals('Bernhard', $form['firstName']->getData()); + $this->assertEquals('Schussek', $form['lastName']->getData()); + } + + public function testSubmitGetRequestWithEmptyRootFormName() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $values = array( + 'firstName' => 'Bernhard', + 'lastName' => 'Schussek', + 'extra' => 'data' + ); + + $request = new Request($values, array(), array(), array(), array(), array( + 'REQUEST_METHOD' => 'GET', + )); + + $form = $this->getBuilder('') + ->setMethod('GET') + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->setRequestHandler(new HttpFoundationRequestHandler()) + ->getForm(); + $form->add($this->getBuilder('firstName')->getForm()); + $form->add($this->getBuilder('lastName')->getForm()); + + $form->handleRequest($request); + + $this->assertEquals('Bernhard', $form['firstName']->getData()); + $this->assertEquals('Schussek', $form['lastName']->getData()); + $this->assertEquals(array('extra' => 'data'), $form->getExtraData()); + } + + public function testGetErrorsAsStringDeep() + { + $parent = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + + $this->form->addError(new FormError('Error!')); + + $parent->add($this->form); + $parent->add($this->getBuilder('foo')->getForm()); + + $this->assertEquals("name:\n ERROR: Error!\nfoo:\n No errors\n", $parent->getErrorsAsString()); + } + + protected function createForm() + { + return $this->getBuilder() + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/ChoiceListTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/ChoiceListTest.php new file mode 100644 index 00000000..63eae9bf --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/ChoiceListTest.php @@ -0,0 +1,200 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\ChoiceList; + +use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceList; +use Symfony\Component\Form\Extension\Core\View\ChoiceView; + +class ChoiceListTest extends \PHPUnit_Framework_TestCase +{ + private $obj1; + + private $obj2; + + private $obj3; + + private $obj4; + + private $list; + + protected function setUp() + { + parent::setUp(); + + $this->obj1 = new \stdClass(); + $this->obj2 = new \stdClass(); + $this->obj3 = new \stdClass(); + $this->obj4 = new \stdClass(); + + $this->list = new ChoiceList( + array( + 'Group 1' => array($this->obj1, $this->obj2), + 'Group 2' => array($this->obj3, $this->obj4), + ), + array( + 'Group 1' => array('A', 'B'), + 'Group 2' => array('C', 'D'), + ), + array($this->obj2, $this->obj3) + ); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->obj1 = null; + $this->obj2 = null; + $this->obj3 = null; + $this->obj4 = null; + $this->list = null; + } + + public function testInitArray() + { + $this->list = new ChoiceList( + array($this->obj1, $this->obj2, $this->obj3, $this->obj4), + array('A', 'B', 'C', 'D'), + array($this->obj2) + ); + + $this->assertSame(array($this->obj1, $this->obj2, $this->obj3, $this->obj4), $this->list->getChoices()); + $this->assertSame(array('0', '1', '2', '3'), $this->list->getValues()); + $this->assertEquals(array(1 => new ChoiceView($this->obj2, '1', 'B')), $this->list->getPreferredViews()); + $this->assertEquals(array(0 => new ChoiceView($this->obj1, '0', 'A'), 2 => new ChoiceView($this->obj3, '2', 'C'), 3 => new ChoiceView($this->obj4, '3', 'D')), $this->list->getRemainingViews()); + } + + /** + * Necessary for interoperability with MongoDB cursors or ORM relations as + * choices parameter. A choice itself that is an object implementing \Traversable + * is not treated as hierarchical structure, but as-is. + */ + public function testInitNestedTraversable() + { + $traversableChoice = new \ArrayIterator(array($this->obj3, $this->obj4)); + + $this->list = new ChoiceList( + new \ArrayIterator(array( + 'Group' => array($this->obj1, $this->obj2), + 'Not a Group' => $traversableChoice + )), + array( + 'Group' => array('A', 'B'), + 'Not a Group' => 'C', + ), + array($this->obj2) + ); + + $this->assertSame(array($this->obj1, $this->obj2, $traversableChoice), $this->list->getChoices()); + $this->assertSame(array('0', '1', '2'), $this->list->getValues()); + $this->assertEquals(array( + 'Group' => array(1 => new ChoiceView($this->obj2, '1', 'B')) + ), $this->list->getPreferredViews()); + $this->assertEquals(array( + 'Group' => array(0 => new ChoiceView($this->obj1, '0', 'A')), + 2 => new ChoiceView($traversableChoice, '2', 'C') + ), $this->list->getRemainingViews()); + } + + public function testInitNestedArray() + { + $this->assertSame(array($this->obj1, $this->obj2, $this->obj3, $this->obj4), $this->list->getChoices()); + $this->assertSame(array('0', '1', '2', '3'), $this->list->getValues()); + $this->assertEquals(array( + 'Group 1' => array(1 => new ChoiceView($this->obj2, '1', 'B')), + 'Group 2' => array(2 => new ChoiceView($this->obj3, '2', 'C')) + ), $this->list->getPreferredViews()); + $this->assertEquals(array( + 'Group 1' => array(0 => new ChoiceView($this->obj1, '0', 'A')), + 'Group 2' => array(3 => new ChoiceView($this->obj4, '3', 'D')) + ), $this->list->getRemainingViews()); + } + + public function testGetIndicesForChoices() + { + $choices = array($this->obj2, $this->obj3); + $this->assertSame(array(1, 2), $this->list->getIndicesForChoices($choices)); + } + + public function testGetIndicesForChoicesIgnoresNonExistingChoices() + { + $choices = array($this->obj2, $this->obj3, 'foobar'); + $this->assertSame(array(1, 2), $this->list->getIndicesForChoices($choices)); + } + + public function testGetIndicesForValues() + { + // values and indices are always the same + $values = array('1', '2'); + $this->assertSame(array(1, 2), $this->list->getIndicesForValues($values)); + } + + public function testGetIndicesForValuesIgnoresNonExistingValues() + { + $values = array('1', '2', '5'); + $this->assertSame(array(1, 2), $this->list->getIndicesForValues($values)); + } + + public function testGetChoicesForValues() + { + $values = array('1', '2'); + $this->assertSame(array($this->obj2, $this->obj3), $this->list->getChoicesForValues($values)); + } + + public function testGetChoicesForValuesCorrectOrderingOfResult() + { + $values = array('2', '1'); + $this->assertSame(array($this->obj3, $this->obj2), $this->list->getChoicesForValues($values)); + } + + public function testGetChoicesForValuesIgnoresNonExistingValues() + { + $values = array('1', '2', '5'); + $this->assertSame(array($this->obj2, $this->obj3), $this->list->getChoicesForValues($values)); + } + + public function testGetValuesForChoices() + { + $choices = array($this->obj2, $this->obj3); + $this->assertSame(array('1', '2'), $this->list->getValuesForChoices($choices)); + } + + public function testGetValuesForChoicesIgnoresNonExistingChoices() + { + $choices = array($this->obj2, $this->obj3, 'foobar'); + $this->assertSame(array('1', '2'), $this->list->getValuesForChoices($choices)); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testNonMatchingLabels() + { + $this->list = new ChoiceList( + array($this->obj1, $this->obj2), + array('A') + ); + } + + public function testLabelsContainingNull() + { + $this->list = new ChoiceList( + array($this->obj1, $this->obj2), + array('A', null) + ); + + $this->assertEquals( + array(0 => new ChoiceView($this->obj1, '0', 'A'), 1 => new ChoiceView($this->obj2, '1', null)), + $this->list->getRemainingViews() + ); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/LazyChoiceListTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/LazyChoiceListTest.php new file mode 100644 index 00000000..bcd309e0 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/LazyChoiceListTest.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\ChoiceList; + +use Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList; +use Symfony\Component\Form\Extension\Core\ChoiceList\LazyChoiceList; +use Symfony\Component\Form\Extension\Core\View\ChoiceView; + +class LazyChoiceListTest extends \PHPUnit_Framework_TestCase +{ + private $list; + + protected function setUp() + { + parent::setUp(); + + $this->list = new LazyChoiceListTest_Impl(new SimpleChoiceList(array( + 'a' => 'A', + 'b' => 'B', + 'c' => 'C', + ), array('b'))); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->list = null; + } + + public function testGetChoices() + { + $this->assertSame(array(0 => 'a', 1 => 'b', 2 => 'c'), $this->list->getChoices()); + } + + public function testGetValues() + { + $this->assertSame(array(0 => 'a', 1 => 'b', 2 => 'c'), $this->list->getValues()); + } + + public function testGetPreferredViews() + { + $this->assertEquals(array(1 => new ChoiceView('b', 'b', 'B')), $this->list->getPreferredViews()); + } + + public function testGetRemainingViews() + { + $this->assertEquals(array(0 => new ChoiceView('a', 'a', 'A'), 2 => new ChoiceView('c', 'c', 'C')), $this->list->getRemainingViews()); + } + + public function testGetIndicesForChoices() + { + $choices = array('b', 'c'); + $this->assertSame(array(1, 2), $this->list->getIndicesForChoices($choices)); + } + + public function testGetIndicesForValues() + { + $values = array('b', 'c'); + $this->assertSame(array(1, 2), $this->list->getIndicesForValues($values)); + } + + public function testGetChoicesForValues() + { + $values = array('b', 'c'); + $this->assertSame(array('b', 'c'), $this->list->getChoicesForValues($values)); + } + + public function testGetValuesForChoices() + { + $choices = array('b', 'c'); + $this->assertSame(array('b', 'c'), $this->list->getValuesForChoices($choices)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException + */ + public function testLoadChoiceListShouldReturnChoiceList() + { + $list = new LazyChoiceListTest_InvalidImpl(); + + $list->getChoices(); + } +} + +class LazyChoiceListTest_Impl extends LazyChoiceList +{ + private $choiceList; + + public function __construct($choiceList) + { + $this->choiceList = $choiceList; + } + + protected function loadChoiceList() + { + return $this->choiceList; + } +} + +class LazyChoiceListTest_InvalidImpl extends LazyChoiceList +{ + protected function loadChoiceList() + { + return new \stdClass(); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/ObjectChoiceListTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/ObjectChoiceListTest.php new file mode 100644 index 00000000..69c5aa0f --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/ObjectChoiceListTest.php @@ -0,0 +1,212 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\ChoiceList; + +use Symfony\Component\Form\Extension\Core\ChoiceList\ObjectChoiceList; +use Symfony\Component\Form\Extension\Core\View\ChoiceView; + +class ObjectChoiceListTest_EntityWithToString +{ + private $property; + + public function __construct($property) + { + $this->property = $property; + } + + public function __toString() + { + return $this->property; + } +} + +class ObjectChoiceListTest extends \PHPUnit_Framework_TestCase +{ + private $obj1; + + private $obj2; + + private $obj3; + + private $obj4; + + /** + * @var ObjectChoiceList + */ + private $list; + + protected function setUp() + { + parent::setUp(); + + $this->obj1 = (object) array('name' => 'A'); + $this->obj2 = (object) array('name' => 'B'); + $this->obj3 = (object) array('name' => 'C'); + $this->obj4 = (object) array('name' => 'D'); + + $this->list = new ObjectChoiceList( + array( + 'Group 1' => array($this->obj1, $this->obj2), + 'Group 2' => array($this->obj3, $this->obj4), + ), + 'name', + array($this->obj2, $this->obj3) + ); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->obj1 = null; + $this->obj2 = null; + $this->obj3 = null; + $this->obj4 = null; + $this->list = null; + } + + public function testInitArray() + { + $this->list = new ObjectChoiceList( + array($this->obj1, $this->obj2, $this->obj3, $this->obj4), + 'name', + array($this->obj2) + ); + + $this->assertSame(array($this->obj1, $this->obj2, $this->obj3, $this->obj4), $this->list->getChoices()); + $this->assertSame(array('0', '1', '2', '3'), $this->list->getValues()); + $this->assertEquals(array(1 => new ChoiceView($this->obj2, '1', 'B')), $this->list->getPreferredViews()); + $this->assertEquals(array(0 => new ChoiceView($this->obj1, '0', 'A'), 2 => new ChoiceView($this->obj3, '2', 'C'), 3 => new ChoiceView($this->obj4, '3', 'D')), $this->list->getRemainingViews()); + } + + public function testInitNestedArray() + { + $this->assertSame(array($this->obj1, $this->obj2, $this->obj3, $this->obj4), $this->list->getChoices()); + $this->assertSame(array('0', '1', '2', '3'), $this->list->getValues()); + $this->assertEquals(array( + 'Group 1' => array(1 => new ChoiceView($this->obj2, '1', 'B')), + 'Group 2' => array(2 => new ChoiceView($this->obj3, '2', 'C')) + ), $this->list->getPreferredViews()); + $this->assertEquals(array( + 'Group 1' => array(0 => new ChoiceView($this->obj1, '0', 'A')), + 'Group 2' => array(3 => new ChoiceView($this->obj4, '3', 'D')) + ), $this->list->getRemainingViews()); + } + + public function testInitArrayWithGroupPath() + { + $this->obj1 = (object) array('name' => 'A', 'category' => 'Group 1'); + $this->obj2 = (object) array('name' => 'B', 'category' => 'Group 1'); + $this->obj3 = (object) array('name' => 'C', 'category' => 'Group 2'); + $this->obj4 = (object) array('name' => 'D', 'category' => 'Group 2'); + + // Objects with NULL groups are not grouped + $obj5 = (object) array('name' => 'E', 'category' => null); + + // Objects without the group property are not grouped either + // see https://github.com/symfony/symfony/commit/d9b7abb7c7a0f28e0ce970afc5e305dce5dccddf + $obj6 = (object) array('name' => 'F'); + + $this->list = new ObjectChoiceList( + array($this->obj1, $this->obj2, $this->obj3, $this->obj4, $obj5, $obj6), + 'name', + array($this->obj2, $this->obj3), + 'category' + ); + + $this->assertSame(array($this->obj1, $this->obj2, $this->obj3, $this->obj4, $obj5, $obj6), $this->list->getChoices()); + $this->assertSame(array('0', '1', '2', '3', '4', '5'), $this->list->getValues()); + $this->assertEquals(array( + 'Group 1' => array(1 => new ChoiceView($this->obj2, '1', 'B')), + 'Group 2' => array(2 => new ChoiceView($this->obj3, '2', 'C')) + ), $this->list->getPreferredViews()); + $this->assertEquals(array( + 'Group 1' => array(0 => new ChoiceView($this->obj1, '0', 'A')), + 'Group 2' => array(3 => new ChoiceView($this->obj4, '3', 'D')), + 4 => new ChoiceView($obj5, '4', 'E'), + 5 => new ChoiceView($obj6, '5', 'F'), + ), $this->list->getRemainingViews()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testInitArrayWithGroupPathThrowsExceptionIfNestedArray() + { + $this->obj1 = (object) array('name' => 'A', 'category' => 'Group 1'); + $this->obj2 = (object) array('name' => 'B', 'category' => 'Group 1'); + $this->obj3 = (object) array('name' => 'C', 'category' => 'Group 2'); + $this->obj4 = (object) array('name' => 'D', 'category' => 'Group 2'); + + new ObjectChoiceList( + array( + 'Group 1' => array($this->obj1, $this->obj2), + 'Group 2' => array($this->obj3, $this->obj4), + ), + 'name', + array($this->obj2, $this->obj3), + 'category' + ); + } + + public function testInitArrayWithValuePath() + { + $this->obj1 = (object) array('name' => 'A', 'id' => 10); + $this->obj2 = (object) array('name' => 'B', 'id' => 20); + $this->obj3 = (object) array('name' => 'C', 'id' => 30); + $this->obj4 = (object) array('name' => 'D', 'id' => 40); + + $this->list = new ObjectChoiceList( + array($this->obj1, $this->obj2, $this->obj3, $this->obj4), + 'name', + array($this->obj2, $this->obj3), + null, + 'id' + ); + + $this->assertSame(array($this->obj1, $this->obj2, $this->obj3, $this->obj4), $this->list->getChoices()); + $this->assertSame(array('10', '20', '30', '40'), $this->list->getValues()); + $this->assertEquals(array(1 => new ChoiceView($this->obj2, '20', 'B'), 2 => new ChoiceView($this->obj3, '30', 'C')), $this->list->getPreferredViews()); + $this->assertEquals(array(0 => new ChoiceView($this->obj1, '10', 'A'), 3 => new ChoiceView($this->obj4, '40', 'D')), $this->list->getRemainingViews()); + } + + public function testInitArrayUsesToString() + { + $this->obj1 = new ObjectChoiceListTest_EntityWithToString('A'); + $this->obj2 = new ObjectChoiceListTest_EntityWithToString('B'); + $this->obj3 = new ObjectChoiceListTest_EntityWithToString('C'); + $this->obj4 = new ObjectChoiceListTest_EntityWithToString('D'); + + $this->list = new ObjectChoiceList( + array($this->obj1, $this->obj2, $this->obj3, $this->obj4) + ); + + $this->assertSame(array($this->obj1, $this->obj2, $this->obj3, $this->obj4), $this->list->getChoices()); + $this->assertSame(array('0', '1', '2', '3'), $this->list->getValues()); + $this->assertEquals(array(0 => new ChoiceView($this->obj1, '0', 'A'), 1 => new ChoiceView($this->obj2, '1', 'B'), 2 => new ChoiceView($this->obj3, '2', 'C'), 3 => new ChoiceView($this->obj4, '3', 'D')), $this->list->getRemainingViews()); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\StringCastException + */ + public function testInitArrayThrowsExceptionIfToStringNotFound() + { + $this->obj1 = new ObjectChoiceListTest_EntityWithToString('A'); + $this->obj2 = new ObjectChoiceListTest_EntityWithToString('B'); + $this->obj3 = (object) array('name' => 'C'); + $this->obj4 = new ObjectChoiceListTest_EntityWithToString('D'); + + new ObjectChoiceList( + array($this->obj1, $this->obj2, $this->obj3, $this->obj4) + ); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/SimpleChoiceListTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/SimpleChoiceListTest.php new file mode 100644 index 00000000..69d27a18 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/SimpleChoiceListTest.php @@ -0,0 +1,188 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\ChoiceList; + +use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceList; +use Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList; +use Symfony\Component\Form\Extension\Core\View\ChoiceView; + +class SimpleChoiceListTest extends \PHPUnit_Framework_TestCase +{ + private $list; + + private $numericList; + + protected function setUp() + { + parent::setUp(); + + $choices = array( + 'Group 1' => array('a' => 'A', 'b' => 'B'), + 'Group 2' => array('c' => 'C', 'd' => 'D'), + ); + $numericChoices = array( + 'Group 1' => array(0 => 'A', 1 => 'B'), + 'Group 2' => array(2 => 'C', 3 => 'D'), + ); + + $this->list = new SimpleChoiceList($choices, array('b', 'c')); + + // Use COPY_CHOICE strategy to test for the various associated problems + $this->numericList = new SimpleChoiceList($numericChoices, array(1, 2)); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->list = null; + $this->numericList = null; + } + + public function testInitArray() + { + $choices = array('a' => 'A', 'b' => 'B', 'c' => 'C'); + $this->list = new SimpleChoiceList($choices, array('b')); + + $this->assertSame(array(0 => 'a', 1 => 'b', 2 => 'c'), $this->list->getChoices()); + $this->assertSame(array(0 => 'a', 1 => 'b', 2 => 'c'), $this->list->getValues()); + $this->assertEquals(array(1 => new ChoiceView('b', 'b', 'B')), $this->list->getPreferredViews()); + $this->assertEquals(array(0 => new ChoiceView('a', 'a', 'A'), 2 => new ChoiceView('c', 'c', 'C')), $this->list->getRemainingViews()); + } + + public function testInitNestedArray() + { + $this->assertSame(array(0 => 'a', 1 => 'b', 2 => 'c', 3 => 'd'), $this->list->getChoices()); + $this->assertSame(array(0 => 'a', 1 => 'b', 2 => 'c', 3 => 'd'), $this->list->getValues()); + $this->assertEquals(array( + 'Group 1' => array(1 => new ChoiceView('b', 'b', 'B')), + 'Group 2' => array(2 => new ChoiceView('c', 'c', 'C')) + ), $this->list->getPreferredViews()); + $this->assertEquals(array( + 'Group 1' => array(0 => new ChoiceView('a', 'a', 'A')), + 'Group 2' => array(3 => new ChoiceView('d', 'd', 'D')) + ), $this->list->getRemainingViews()); + } + + public function testGetIndicesForChoices() + { + $choices = array('b', 'c'); + $this->assertSame(array(1, 2), $this->list->getIndicesForChoices($choices)); + } + + public function testGetIndicesForChoicesIgnoresNonExistingChoices() + { + $choices = array('b', 'c', 'foobar'); + $this->assertSame(array(1, 2), $this->list->getIndicesForChoices($choices)); + } + + public function testGetIndicesForChoicesDealsWithNumericChoices() + { + // Pass choices as strings although they are integers + $choices = array('0', '1'); + $this->assertSame(array(0, 1), $this->numericList->getIndicesForChoices($choices)); + } + + public function testGetIndicesForValues() + { + $values = array('b', 'c'); + $this->assertSame(array(1, 2), $this->list->getIndicesForValues($values)); + } + + public function testGetIndicesForValuesIgnoresNonExistingValues() + { + $values = array('b', 'c', '100'); + $this->assertSame(array(1, 2), $this->list->getIndicesForValues($values)); + } + + public function testGetIndicesForValuesDealsWithNumericValues() + { + // Pass values as strings although they are integers + $values = array('0', '1'); + $this->assertSame(array(0, 1), $this->numericList->getIndicesForValues($values)); + } + + public function testGetChoicesForValues() + { + $values = array('b', 'c'); + $this->assertSame(array('b', 'c'), $this->list->getChoicesForValues($values)); + } + + public function testGetChoicesForValuesIgnoresNonExistingValues() + { + $values = array('b', 'c', '100'); + $this->assertSame(array('b', 'c'), $this->list->getChoicesForValues($values)); + } + + public function testGetChoicesForValuesDealsWithNumericValues() + { + // Pass values as strings although they are integers + $values = array('0', '1'); + $this->assertSame(array(0, 1), $this->numericList->getChoicesForValues($values)); + } + + public function testGetValuesForChoices() + { + $choices = array('b', 'c'); + $this->assertSame(array('b', 'c'), $this->list->getValuesForChoices($choices)); + } + + public function testGetValuesForChoicesIgnoresNonExistingValues() + { + $choices = array('b', 'c', 'foobar'); + $this->assertSame(array('b', 'c'), $this->list->getValuesForChoices($choices)); + } + + public function testGetValuesForChoicesDealsWithNumericValues() + { + // Pass values as strings although they are integers + $values = array('0', '1'); + + $this->assertSame(array('0', '1'), $this->numericList->getValuesForChoices($values)); + } + + /** + * @dataProvider dirtyValuesProvider + */ + public function testGetValuesForChoicesDealsWithDirtyValues($choice, $value) + { + $choices = array( + '0' => 'Zero', + '1' => 'One', + '' => 'Empty', + '1.23' => 'Float', + 'foo' => 'Foo', + 'foo10' => 'Foo 10', + ); + + // use COPY_CHOICE strategy to test the problems + $this->list = new SimpleChoiceList($choices, array()); + + $this->assertSame(array($value), $this->list->getValuesForChoices(array($choice))); + } + + public function dirtyValuesProvider() + { + return array( + array(0, '0'), + array('0', '0'), + array('1', '1'), + array(false, '0'), + array(true, '1'), + array('', ''), + array(null, ''), + array('1.23', '1.23'), + array('foo', 'foo'), + array('foo10', 'foo10'), + ); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php new file mode 100644 index 00000000..ee2e3351 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php @@ -0,0 +1,319 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataMapper; + +use Symfony\Component\Form\FormConfigBuilder; +use Symfony\Component\Form\FormConfigInterface; +use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper; + +class PropertyPathMapperTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var PropertyPathMapper + */ + private $mapper; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $dispatcher; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $propertyAccessor; + + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\Event')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + if (!class_exists('Symfony\Component\PropertyAccess\PropertyAccess')) { + $this->markTestSkipped('The "PropertyAccess" component is not available'); + } + + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->propertyAccessor = $this->getMock('Symfony\Component\PropertyAccess\PropertyAccessorInterface'); + $this->mapper = new PropertyPathMapper($this->propertyAccessor); + } + + /** + * @param $path + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getPropertyPath($path) + { + return $this->getMockBuilder('Symfony\Component\PropertyAccess\PropertyPath') + ->setConstructorArgs(array($path)) + ->setMethods(array('getValue', 'setValue')) + ->getMock(); + } + + /** + * @param FormConfigInterface $config + * @param Boolean $synchronized + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getForm(FormConfigInterface $config, $synchronized = true) + { + $form = $this->getMockBuilder('Symfony\Component\Form\Form') + ->setConstructorArgs(array($config)) + ->setMethods(array('isSynchronized')) + ->getMock(); + + $form->expects($this->any()) + ->method('isSynchronized') + ->will($this->returnValue($synchronized)); + + return $form; + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getDataMapper() + { + return $this->getMock('Symfony\Component\Form\DataMapperInterface'); + } + + public function testMapDataToFormsPassesObjectRefIfByReference() + { + $car = new \stdClass(); + $engine = new \stdClass(); + $propertyPath = $this->getPropertyPath('engine'); + + $this->propertyAccessor->expects($this->once()) + ->method('getValue') + ->with($car, $propertyPath) + ->will($this->returnValue($engine)); + + $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $form = $this->getForm($config); + + $this->mapper->mapDataToForms($car, array($form)); + + // Can't use isIdentical() above because mocks always clone their + // arguments which can't be disabled in PHPUnit 3.6 + $this->assertSame($engine, $form->getData()); + } + + public function testMapDataToFormsPassesObjectCloneIfNotByReference() + { + $car = new \stdClass(); + $engine = new \stdClass(); + $propertyPath = $this->getPropertyPath('engine'); + + $this->propertyAccessor->expects($this->once()) + ->method('getValue') + ->with($car, $propertyPath) + ->will($this->returnValue($engine)); + + $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher); + $config->setByReference(false); + $config->setPropertyPath($propertyPath); + $form = $this->getForm($config); + + $this->mapper->mapDataToForms($car, array($form)); + + $this->assertNotSame($engine, $form->getData()); + $this->assertEquals($engine, $form->getData()); + } + + public function testMapDataToFormsIgnoresEmptyPropertyPath() + { + $car = new \stdClass(); + + $config = new FormConfigBuilder(null, '\stdClass', $this->dispatcher); + $config->setByReference(true); + $form = $this->getForm($config); + + $this->assertNull($form->getPropertyPath()); + + $this->mapper->mapDataToForms($car, array($form)); + + $this->assertNull($form->getData()); + } + + public function testMapDataToFormsIgnoresUnmapped() + { + $car = new \stdClass(); + $propertyPath = $this->getPropertyPath('engine'); + + $this->propertyAccessor->expects($this->never()) + ->method('getValue'); + + $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher); + $config->setByReference(true); + $config->setMapped(false); + $config->setPropertyPath($propertyPath); + $form = $this->getForm($config); + + $this->mapper->mapDataToForms($car, array($form)); + + $this->assertNull($form->getData()); + } + + public function testMapDataToFormsIgnoresEmptyData() + { + $propertyPath = $this->getPropertyPath('engine'); + + $this->propertyAccessor->expects($this->never()) + ->method('getValue'); + + $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $form = $this->getForm($config); + + $this->mapper->mapDataToForms(null, array($form)); + + $this->assertNull($form->getData()); + } + + public function testMapFormsToDataWritesBackIfNotByReference() + { + $car = new \stdClass(); + $engine = new \stdClass(); + $propertyPath = $this->getPropertyPath('engine'); + + $this->propertyAccessor->expects($this->once()) + ->method('setValue') + ->with($car, $propertyPath, $engine); + + $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher); + $config->setByReference(false); + $config->setPropertyPath($propertyPath); + $config->setData($engine); + $form = $this->getForm($config); + + $this->mapper->mapFormsToData(array($form), $car); + } + + public function testMapFormsToDataWritesBackIfByReferenceButNoReference() + { + $car = new \stdClass(); + $engine = new \stdClass(); + $propertyPath = $this->getPropertyPath('engine'); + + $this->propertyAccessor->expects($this->once()) + ->method('setValue') + ->with($car, $propertyPath, $engine); + + $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $config->setData($engine); + $form = $this->getForm($config); + + $this->mapper->mapFormsToData(array($form), $car); + } + + public function testMapFormsToDataWritesBackIfByReferenceAndReference() + { + $car = new \stdClass(); + $engine = new \stdClass(); + $propertyPath = $this->getPropertyPath('engine'); + + // $car already contains the reference of $engine + $this->propertyAccessor->expects($this->once()) + ->method('getValue') + ->with($car, $propertyPath) + ->will($this->returnValue($engine)); + + $this->propertyAccessor->expects($this->never()) + ->method('setValue'); + + $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $config->setData($engine); + $form = $this->getForm($config); + + $this->mapper->mapFormsToData(array($form), $car); + } + + public function testMapFormsToDataIgnoresUnmapped() + { + $car = new \stdClass(); + $engine = new \stdClass(); + $propertyPath = $this->getPropertyPath('engine'); + + $this->propertyAccessor->expects($this->never()) + ->method('setValue'); + + $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $config->setData($engine); + $config->setMapped(false); + $form = $this->getForm($config); + + $this->mapper->mapFormsToData(array($form), $car); + } + + public function testMapFormsToDataIgnoresEmptyData() + { + $car = new \stdClass(); + $propertyPath = $this->getPropertyPath('engine'); + + $this->propertyAccessor->expects($this->never()) + ->method('setValue'); + + $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $config->setData(null); + $form = $this->getForm($config); + + $this->mapper->mapFormsToData(array($form), $car); + } + + public function testMapFormsToDataIgnoresUnsynchronized() + { + $car = new \stdClass(); + $engine = new \stdClass(); + $propertyPath = $this->getPropertyPath('engine'); + + $this->propertyAccessor->expects($this->never()) + ->method('setValue'); + + $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $config->setData($engine); + $form = $this->getForm($config, false); + + $this->mapper->mapFormsToData(array($form), $car); + } + + public function testMapFormsToDataIgnoresDisabled() + { + $car = new \stdClass(); + $engine = new \stdClass(); + $propertyPath = $this->getPropertyPath('engine'); + + $this->propertyAccessor->expects($this->never()) + ->method('setValue'); + + $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $config->setData($engine); + $config->setDisabled(true); + $form = $this->getForm($config); + + $this->mapper->mapFormsToData(array($form), $car); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ArrayToPartsTransformerTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ArrayToPartsTransformerTest.php new file mode 100644 index 00000000..bafe5c09 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ArrayToPartsTransformerTest.php @@ -0,0 +1,149 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\ArrayToPartsTransformer; + +class ArrayToPartsTransformerTest extends \PHPUnit_Framework_TestCase +{ + private $transformer; + + protected function setUp() + { + $this->transformer = new ArrayToPartsTransformer(array( + 'first' => array('a', 'b', 'c'), + 'second' => array('d', 'e', 'f'), + )); + } + + protected function tearDown() + { + $this->transformer = null; + } + + public function testTransform() + { + $input = array( + 'a' => '1', + 'b' => '2', + 'c' => '3', + 'd' => '4', + 'e' => '5', + 'f' => '6', + ); + + $output = array( + 'first' => array( + 'a' => '1', + 'b' => '2', + 'c' => '3', + ), + 'second' => array( + 'd' => '4', + 'e' => '5', + 'f' => '6', + ), + ); + + $this->assertSame($output, $this->transformer->transform($input)); + } + + public function testTransformEmpty() + { + $output = array( + 'first' => null, + 'second' => null, + ); + + $this->assertSame($output, $this->transformer->transform(null)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testTransformRequiresArray() + { + $this->transformer->transform('12345'); + } + + public function testReverseTransform() + { + $input = array( + 'first' => array( + 'a' => '1', + 'b' => '2', + 'c' => '3', + ), + 'second' => array( + 'd' => '4', + 'e' => '5', + 'f' => '6', + ), + ); + + $output = array( + 'a' => '1', + 'b' => '2', + 'c' => '3', + 'd' => '4', + 'e' => '5', + 'f' => '6', + ); + + $this->assertSame($output, $this->transformer->reverseTransform($input)); + } + + public function testReverseTransformCompletelyEmpty() + { + $input = array( + 'first' => '', + 'second' => '', + ); + + $this->assertNull($this->transformer->reverseTransform($input)); + } + + public function testReverseTransformCompletelyNull() + { + $input = array( + 'first' => null, + 'second' => null, + ); + + $this->assertNull($this->transformer->reverseTransform($input)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformPartiallyNull() + { + $input = array( + 'first' => array( + 'a' => '1', + 'b' => '2', + 'c' => '3', + ), + 'second' => null, + ); + + $this->transformer->reverseTransform($input); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformRequiresArray() + { + $this->transformer->reverseTransform('12345'); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BooleanToStringTransformerTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BooleanToStringTransformerTest.php new file mode 100644 index 00000000..41f8f956 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BooleanToStringTransformerTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\BooleanToStringTransformer; + +class BooleanToStringTransformerTest extends \PHPUnit_Framework_TestCase +{ + const TRUE_VALUE = '1'; + + protected $transformer; + + protected function setUp() + { + $this->transformer = new BooleanToStringTransformer(self::TRUE_VALUE); + } + + protected function tearDown() + { + $this->transformer = null; + } + + public function testTransform() + { + $this->assertEquals(self::TRUE_VALUE, $this->transformer->transform(true)); + $this->assertNull($this->transformer->transform(false)); + $this->assertNull($this->transformer->transform(null)); + } + + public function testTransformExpectsBoolean() + { + $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException'); + + $this->transformer->transform('1'); + } + + public function testReverseTransformExpectsString() + { + $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException'); + + $this->transformer->reverseTransform(1); + } + + public function testReverseTransform() + { + $this->assertTrue($this->transformer->reverseTransform(self::TRUE_VALUE)); + $this->assertTrue($this->transformer->reverseTransform('foobar')); + $this->assertTrue($this->transformer->reverseTransform('')); + $this->assertFalse($this->transformer->reverseTransform(null)); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoiceToValueTransformerTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoiceToValueTransformerTest.php new file mode 100644 index 00000000..bbae0621 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoiceToValueTransformerTest.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList; +use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToValueTransformer; + +class ChoiceToValueTransformerTest extends \PHPUnit_Framework_TestCase +{ + protected $transformer; + + protected function setUp() + { + $list = new SimpleChoiceList(array('' => 'A', 0 => 'B', 1 => 'C')); + $this->transformer = new ChoiceToValueTransformer($list); + } + + protected function tearDown() + { + $this->transformer = null; + } + + public function transformProvider() + { + return array( + // more extensive test set can be found in FormUtilTest + array(0, '0'), + array(false, '0'), + array('', ''), + ); + } + + /** + * @dataProvider transformProvider + */ + public function testTransform($in, $out) + { + $this->assertSame($out, $this->transformer->transform($in)); + } + + public function reverseTransformProvider() + { + return array( + // values are expected to be valid choice keys already and stay + // the same + array('0', 0), + array('', null), + array(null, null), + ); + } + + /** + * @dataProvider reverseTransformProvider + */ + public function testReverseTransform($in, $out) + { + $this->assertSame($out, $this->transformer->reverseTransform($in)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformExpectsScalar() + { + $this->transformer->reverseTransform(array()); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoicesToValuesTransformerTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoicesToValuesTransformerTest.php new file mode 100644 index 00000000..57297193 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoicesToValuesTransformerTest.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList; + +use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToValuesTransformer; + +class ChoicesToValuesTransformerTest extends \PHPUnit_Framework_TestCase +{ + protected $transformer; + + protected function setUp() + { + $list = new SimpleChoiceList(array(0 => 'A', 1 => 'B', 2 => 'C')); + $this->transformer = new ChoicesToValuesTransformer($list); + } + + protected function tearDown() + { + $this->transformer = null; + } + + public function testTransform() + { + // Value strategy in SimpleChoiceList is to copy and convert to string + $in = array(0, 1, 2); + $out = array('0', '1', '2'); + + $this->assertSame($out, $this->transformer->transform($in)); + } + + public function testTransformNull() + { + $this->assertSame(array(), $this->transformer->transform(null)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testTransformExpectsArray() + { + $this->transformer->transform('foobar'); + } + + public function testReverseTransform() + { + // values are expected to be valid choices and stay the same + $in = array('0', '1', '2'); + $out = array(0, 1, 2); + + $this->assertSame($out, $this->transformer->reverseTransform($in)); + } + + public function testReverseTransformNull() + { + $this->assertSame(array(), $this->transformer->reverseTransform(null)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformExpectsArray() + { + $this->transformer->reverseTransform('foobar'); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DataTransformerChainTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DataTransformerChainTest.php new file mode 100644 index 00000000..2ee2f22d --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DataTransformerChainTest.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\DataTransformerChain; + +class DataTransformerChainTest extends \PHPUnit_Framework_TestCase +{ + public function testTransform() + { + $transformer1 = $this->getMock('Symfony\Component\Form\DataTransformerInterface'); + $transformer1->expects($this->once()) + ->method('transform') + ->with($this->identicalTo('foo')) + ->will($this->returnValue('bar')); + $transformer2 = $this->getMock('Symfony\Component\Form\DataTransformerInterface'); + $transformer2->expects($this->once()) + ->method('transform') + ->with($this->identicalTo('bar')) + ->will($this->returnValue('baz')); + + $chain = new DataTransformerChain(array($transformer1, $transformer2)); + + $this->assertEquals('baz', $chain->transform('foo')); + } + + public function testReverseTransform() + { + $transformer2 = $this->getMock('Symfony\Component\Form\DataTransformerInterface'); + $transformer2->expects($this->once()) + ->method('reverseTransform') + ->with($this->identicalTo('foo')) + ->will($this->returnValue('bar')); + $transformer1 = $this->getMock('Symfony\Component\Form\DataTransformerInterface'); + $transformer1->expects($this->once()) + ->method('reverseTransform') + ->with($this->identicalTo('bar')) + ->will($this->returnValue('baz')); + + $chain = new DataTransformerChain(array($transformer1, $transformer2)); + + $this->assertEquals('baz', $chain->reverseTransform('foo')); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeTestCase.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeTestCase.php new file mode 100644 index 00000000..f7722c49 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeTestCase.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +abstract class DateTimeTestCase extends \PHPUnit_Framework_TestCase +{ + public static function assertDateTimeEquals(\DateTime $expected, \DateTime $actual) + { + self::assertEquals($expected->format('c'), $actual->format('c')); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToArrayTransformerTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToArrayTransformerTest.php new file mode 100644 index 00000000..4898b88d --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToArrayTransformerTest.php @@ -0,0 +1,512 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer; + +class DateTimeToArrayTransformerTest extends DateTimeTestCase +{ + public function testTransform() + { + $transformer = new DateTimeToArrayTransformer('UTC', 'UTC'); + + $input = new \DateTime('2010-02-03 04:05:06 UTC'); + + $output = array( + 'year' => '2010', + 'month' => '2', + 'day' => '3', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + ); + + $this->assertSame($output, $transformer->transform($input)); + } + + public function testTransformEmpty() + { + $transformer = new DateTimeToArrayTransformer(); + + $output = array( + 'year' => '', + 'month' => '', + 'day' => '', + 'hour' => '', + 'minute' => '', + 'second' => '', + ); + + $this->assertSame($output, $transformer->transform(null)); + } + + public function testTransformEmptyWithFields() + { + $transformer = new DateTimeToArrayTransformer(null, null, array('year', 'minute', 'second')); + + $output = array( + 'year' => '', + 'minute' => '', + 'second' => '', + ); + + $this->assertSame($output, $transformer->transform(null)); + } + + public function testTransformWithFields() + { + $transformer = new DateTimeToArrayTransformer('UTC', 'UTC', array('year', 'month', 'minute', 'second')); + + $input = new \DateTime('2010-02-03 04:05:06 UTC'); + + $output = array( + 'year' => '2010', + 'month' => '2', + 'minute' => '5', + 'second' => '6', + ); + + $this->assertSame($output, $transformer->transform($input)); + } + + public function testTransformWithPadding() + { + $transformer = new DateTimeToArrayTransformer('UTC', 'UTC', null, true); + + $input = new \DateTime('2010-02-03 04:05:06 UTC'); + + $output = array( + 'year' => '2010', + 'month' => '02', + 'day' => '03', + 'hour' => '04', + 'minute' => '05', + 'second' => '06', + ); + + $this->assertSame($output, $transformer->transform($input)); + } + + public function testTransformDifferentTimezones() + { + $transformer = new DateTimeToArrayTransformer('America/New_York', 'Asia/Hong_Kong'); + + $input = new \DateTime('2010-02-03 04:05:06 America/New_York'); + + $dateTime = new \DateTime('2010-02-03 04:05:06 America/New_York'); + $dateTime->setTimezone(new \DateTimeZone('Asia/Hong_Kong')); + $output = array( + 'year' => (string) (int) $dateTime->format('Y'), + 'month' => (string) (int) $dateTime->format('m'), + 'day' => (string) (int) $dateTime->format('d'), + 'hour' => (string) (int) $dateTime->format('H'), + 'minute' => (string) (int) $dateTime->format('i'), + 'second' => (string) (int) $dateTime->format('s'), + ); + + $this->assertSame($output, $transformer->transform($input)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testTransformRequiresDateTime() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform('12345'); + } + + public function testReverseTransform() + { + $transformer = new DateTimeToArrayTransformer('UTC', 'UTC'); + + $input = array( + 'year' => '2010', + 'month' => '2', + 'day' => '3', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + ); + + $output = new \DateTime('2010-02-03 04:05:06 UTC'); + + $this->assertDateTimeEquals($output, $transformer->reverseTransform($input)); + } + + public function testReverseTransformWithSomeZero() + { + $transformer = new DateTimeToArrayTransformer('UTC', 'UTC'); + + $input = array( + 'year' => '2010', + 'month' => '2', + 'day' => '3', + 'hour' => '4', + 'minute' => '0', + 'second' => '0', + ); + + $output = new \DateTime('2010-02-03 04:00:00 UTC'); + + $this->assertDateTimeEquals($output, $transformer->reverseTransform($input)); + } + + public function testReverseTransformCompletelyEmpty() + { + $transformer = new DateTimeToArrayTransformer(); + + $input = array( + 'year' => '', + 'month' => '', + 'day' => '', + 'hour' => '', + 'minute' => '', + 'second' => '', + ); + + $this->assertNull($transformer->reverseTransform($input)); + } + + public function testReverseTransformCompletelyEmptySubsetOfFields() + { + $transformer = new DateTimeToArrayTransformer(null, null, array('year', 'month', 'day')); + + $input = array( + 'year' => '', + 'month' => '', + 'day' => '', + ); + + $this->assertNull($transformer->reverseTransform($input)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformPartiallyEmptyYear() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'month' => '2', + 'day' => '3', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformPartiallyEmptyMonth() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'day' => '3', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformPartiallyEmptyDay() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '2', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformPartiallyEmptyHour() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '2', + 'day' => '3', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformPartiallyEmptyMinute() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '2', + 'day' => '3', + 'hour' => '4', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformPartiallyEmptySecond() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '2', + 'day' => '3', + 'hour' => '4', + 'minute' => '5', + )); + } + + public function testReverseTransformNull() + { + $transformer = new DateTimeToArrayTransformer(); + + $this->assertNull($transformer->reverseTransform(null)); + } + + public function testReverseTransformDifferentTimezones() + { + $transformer = new DateTimeToArrayTransformer('America/New_York', 'Asia/Hong_Kong'); + + $input = array( + 'year' => '2010', + 'month' => '2', + 'day' => '3', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + ); + + $output = new \DateTime('2010-02-03 04:05:06 Asia/Hong_Kong'); + $output->setTimezone(new \DateTimeZone('America/New_York')); + + $this->assertDateTimeEquals($output, $transformer->reverseTransform($input)); + } + + public function testReverseTransformToDifferentTimezone() + { + $transformer = new DateTimeToArrayTransformer('Asia/Hong_Kong', 'UTC'); + + $input = array( + 'year' => '2010', + 'month' => '2', + 'day' => '3', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + ); + + $output = new \DateTime('2010-02-03 04:05:06 UTC'); + $output->setTimezone(new \DateTimeZone('Asia/Hong_Kong')); + + $this->assertDateTimeEquals($output, $transformer->reverseTransform($input)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformRequiresArray() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform('12345'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithNegativeYear() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '-1', + 'month' => '2', + 'day' => '3', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithNegativeMonth() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '-1', + 'day' => '3', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithNegativeDay() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '2', + 'day' => '-1', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithNegativeHour() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '2', + 'day' => '3', + 'hour' => '-1', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithNegativeMinute() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '2', + 'day' => '3', + 'hour' => '4', + 'minute' => '-1', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithNegativeSecond() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '2', + 'day' => '3', + 'hour' => '4', + 'minute' => '5', + 'second' => '-1', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithInvalidMonth() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '13', + 'day' => '3', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithInvalidDay() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '2', + 'day' => '31', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithStringDay() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '2', + 'day' => 'bazinga', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithStringMonth() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => 'bazinga', + 'day' => '31', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithStringYear() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => 'bazinga', + 'month' => '2', + 'day' => '31', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + )); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php new file mode 100644 index 00000000..cb50fc36 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php @@ -0,0 +1,275 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class DateTimeToLocalizedStringTransformerTest extends DateTimeTestCase +{ + protected $dateTime; + protected $dateTimeWithoutSeconds; + + protected function setUp() + { + parent::setUp(); + + // Since we test against "de_AT", we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + + $this->dateTime = new \DateTime('2010-02-03 04:05:06 UTC'); + $this->dateTimeWithoutSeconds = new \DateTime('2010-02-03 04:05:00 UTC'); + } + + protected function tearDown() + { + $this->dateTime = null; + $this->dateTimeWithoutSeconds = null; + } + + public static function assertEquals($expected, $actual, $message = '', $delta = 0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false) + { + if ($expected instanceof \DateTime && $actual instanceof \DateTime) { + $expected = $expected->format('c'); + $actual = $actual->format('c'); + } + + parent::assertEquals($expected, $actual, $message, $delta, $maxDepth, $canonicalize, $ignoreCase); + } + + public function dataProvider() + { + return array( + array(\IntlDateFormatter::SHORT, null, null, '03.02.10 04:05', '2010-02-03 04:05:00 UTC'), + array(\IntlDateFormatter::MEDIUM, null, null, '03.02.2010 04:05', '2010-02-03 04:05:00 UTC'), + array(\IntlDateFormatter::LONG, null, null, '03. Februar 2010 04:05', '2010-02-03 04:05:00 UTC'), + array(\IntlDateFormatter::FULL, null, null, 'Mittwoch, 03. Februar 2010 04:05', '2010-02-03 04:05:00 UTC'), + array(\IntlDateFormatter::SHORT, \IntlDateFormatter::NONE, null, '03.02.10', '2010-02-03 00:00:00 UTC'), + array(\IntlDateFormatter::MEDIUM, \IntlDateFormatter::NONE, null, '03.02.2010', '2010-02-03 00:00:00 UTC'), + array(\IntlDateFormatter::LONG, \IntlDateFormatter::NONE, null, '03. Februar 2010', '2010-02-03 00:00:00 UTC'), + array(\IntlDateFormatter::FULL, \IntlDateFormatter::NONE, null, 'Mittwoch, 03. Februar 2010', '2010-02-03 00:00:00 UTC'), + array(null, \IntlDateFormatter::SHORT, null, '03.02.2010 04:05', '2010-02-03 04:05:00 UTC'), + array(null, \IntlDateFormatter::MEDIUM, null, '03.02.2010 04:05:06', '2010-02-03 04:05:06 UTC'), + array(null, \IntlDateFormatter::LONG, null, '03.02.2010 04:05:06 GMT', '2010-02-03 04:05:06 UTC'), + // see below for extra test case for time format FULL + array(\IntlDateFormatter::NONE, \IntlDateFormatter::SHORT, null, '04:05', '1970-01-01 04:05:00 UTC'), + array(\IntlDateFormatter::NONE, \IntlDateFormatter::MEDIUM, null, '04:05:06', '1970-01-01 04:05:06 UTC'), + array(\IntlDateFormatter::NONE, \IntlDateFormatter::LONG, null, '04:05:06 GMT', '1970-01-01 04:05:06 UTC'), + array(null, null, 'yyyy-MM-dd HH:mm:00', '2010-02-03 04:05:00', '2010-02-03 04:05:00 UTC'), + array(null, null, 'yyyy-MM-dd HH:mm', '2010-02-03 04:05', '2010-02-03 04:05:00 UTC'), + array(null, null, 'yyyy-MM-dd HH', '2010-02-03 04', '2010-02-03 04:00:00 UTC'), + array(null, null, 'yyyy-MM-dd', '2010-02-03', '2010-02-03 00:00:00 UTC'), + array(null, null, 'yyyy-MM', '2010-02', '2010-02-01 00:00:00 UTC'), + array(null, null, 'yyyy', '2010', '2010-01-01 00:00:00 UTC'), + array(null, null, 'dd-MM-yyyy', '03-02-2010', '2010-02-03 00:00:00 UTC'), + array(null, null, 'HH:mm:ss', '04:05:06', '1970-01-01 04:05:06 UTC'), + array(null, null, 'HH:mm:00', '04:05:00', '1970-01-01 04:05:00 UTC'), + array(null, null, 'HH:mm', '04:05', '1970-01-01 04:05:00 UTC'), + array(null, null, 'HH', '04', '1970-01-01 04:00:00 UTC'), + ); + } + + /** + * @dataProvider dataProvider + */ + public function testTransform($dateFormat, $timeFormat, $pattern, $output, $input) + { + $transformer = new DateTimeToLocalizedStringTransformer( + 'UTC', + 'UTC', + $dateFormat, + $timeFormat, + \IntlDateFormatter::GREGORIAN, + $pattern + ); + + $input = new \DateTime($input); + + $this->assertEquals($output, $transformer->transform($input)); + } + + public function testTransformFullTime() + { + $transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', null, \IntlDateFormatter::FULL); + + $this->assertEquals('03.02.2010 04:05:06 GMT', $transformer->transform($this->dateTime)); + } + + public function testTransformToDifferentLocale() + { + \Locale::setDefault('en_US'); + + $transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC'); + + $this->assertEquals('Feb 3, 2010, 4:05 AM', $transformer->transform($this->dateTime)); + } + + public function testTransformEmpty() + { + $transformer = new DateTimeToLocalizedStringTransformer(); + + $this->assertSame('', $transformer->transform(null)); + } + + public function testTransformWithDifferentTimezones() + { + $transformer = new DateTimeToLocalizedStringTransformer('America/New_York', 'Asia/Hong_Kong'); + + $input = new \DateTime('2010-02-03 04:05:06 America/New_York'); + + $dateTime = clone $input; + $dateTime->setTimezone(new \DateTimeZone('Asia/Hong_Kong')); + + $this->assertEquals($dateTime->format('d.m.Y H:i'), $transformer->transform($input)); + } + + public function testTransformWithDifferentPatterns() + { + $transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', \IntlDateFormatter::FULL, \IntlDateFormatter::FULL, \IntlDateFormatter::GREGORIAN, 'MM*yyyy*dd HH|mm|ss'); + + $this->assertEquals('02*2010*03 04|05|06', $transformer->transform($this->dateTime)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testTransformRequiresValidDateTime() + { + $transformer = new DateTimeToLocalizedStringTransformer(); + $transformer->transform('2010-01-01'); + } + + public function testTransformWrapsIntlErrors() + { + $transformer = new DateTimeToLocalizedStringTransformer(); + + // HOW TO REPRODUCE? + + //$this->setExpectedException('Symfony\Component\Form\Extension\Core\DataTransformer\Transdate_formationFailedException'); + + //$transformer->transform(1.5); + } + + /** + * @dataProvider dataProvider + */ + public function testReverseTransform($dateFormat, $timeFormat, $pattern, $input, $output) + { + $transformer = new DateTimeToLocalizedStringTransformer( + 'UTC', + 'UTC', + $dateFormat, + $timeFormat, + \IntlDateFormatter::GREGORIAN, + $pattern + ); + + $output = new \DateTime($output); + + $this->assertEquals($output, $transformer->reverseTransform($input)); + } + + public function testReverseTransformFullTime() + { + $transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', null, \IntlDateFormatter::FULL); + + $this->assertDateTimeEquals($this->dateTime, $transformer->reverseTransform('03.02.2010 04:05:06 GMT+00:00')); + } + + public function testReverseTransformFromDifferentLocale() + { + \Locale::setDefault('en_US'); + + $transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC'); + + $this->assertDateTimeEquals($this->dateTimeWithoutSeconds, $transformer->reverseTransform('Feb 3, 2010, 04:05 AM')); + } + + public function testReverseTransformWithDifferentTimezones() + { + $transformer = new DateTimeToLocalizedStringTransformer('America/New_York', 'Asia/Hong_Kong'); + + $dateTime = new \DateTime('2010-02-03 04:05:00 Asia/Hong_Kong'); + $dateTime->setTimezone(new \DateTimeZone('America/New_York')); + + $this->assertDateTimeEquals($dateTime, $transformer->reverseTransform('03.02.2010 04:05')); + } + + public function testReverseTransformWithDifferentPatterns() + { + $transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', \IntlDateFormatter::FULL, \IntlDateFormatter::FULL, \IntlDateFormatter::GREGORIAN, 'MM*yyyy*dd HH|mm|ss'); + + $this->assertDateTimeEquals($this->dateTime, $transformer->reverseTransform('02*2010*03 04|05|06')); + } + + public function testReverseTransformEmpty() + { + $transformer = new DateTimeToLocalizedStringTransformer(); + + $this->assertNull($transformer->reverseTransform('')); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformRequiresString() + { + $transformer = new DateTimeToLocalizedStringTransformer(); + $transformer->reverseTransform(12345); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWrapsIntlErrors() + { + $transformer = new DateTimeToLocalizedStringTransformer(); + $transformer->reverseTransform('12345'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + */ + public function testValidateDateFormatOption() + { + new DateTimeToLocalizedStringTransformer(null, null, 'foobar'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + */ + public function testValidateTimeFormatOption() + { + new DateTimeToLocalizedStringTransformer(null, null, null, 'foobar'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithNonExistingDate() + { + $transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', \IntlDateFormatter::SHORT); + + $this->assertDateTimeEquals($this->dateTimeWithoutSeconds, $transformer->reverseTransform('31.04.10 04:05')); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformOutOfTimestampRange() + { + $transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC'); + $transformer->reverseTransform('1789-07-14'); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToRfc3339TransformerTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToRfc3339TransformerTest.php new file mode 100644 index 00000000..98aeb772 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToRfc3339TransformerTest.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToRfc3339Transformer; + +class DateTimeToRfc3339TransformerTest extends DateTimeTestCase +{ + protected $dateTime; + protected $dateTimeWithoutSeconds; + + protected function setUp() + { + parent::setUp(); + + $this->dateTime = new \DateTime('2010-02-03 04:05:06 UTC'); + $this->dateTimeWithoutSeconds = new \DateTime('2010-02-03 04:05:00 UTC'); + } + + protected function tearDown() + { + $this->dateTime = null; + $this->dateTimeWithoutSeconds = null; + } + + public static function assertEquals($expected, $actual, $message = '', $delta = 0, $maxDepth = 10, $canonicalize = FALSE, $ignoreCase = FALSE) + { + if ($expected instanceof \DateTime && $actual instanceof \DateTime) { + $expected = $expected->format('c'); + $actual = $actual->format('c'); + } + + parent::assertEquals($expected, $actual, $message, $delta, $maxDepth, $canonicalize, $ignoreCase); + } + + public function allProvider() + { + return array( + array('UTC', 'UTC', '2010-02-03 04:05:06 UTC', '2010-02-03T04:05:06Z'), + array('UTC', 'UTC', null, ''), + array('America/New_York', 'Asia/Hong_Kong', '2010-02-03 04:05:06 America/New_York', '2010-02-03T17:05:06+08:00'), + array('America/New_York', 'Asia/Hong_Kong', null, ''), + array('UTC', 'Asia/Hong_Kong', '2010-02-03 04:05:06 UTC', '2010-02-03T12:05:06+08:00'), + array('America/New_York', 'UTC', '2010-02-03 04:05:06 America/New_York', '2010-02-03T09:05:06Z'), + ); + } + + public function transformProvider() + { + return $this->allProvider(); + } + + public function reverseTransformProvider() + { + return array_merge($this->allProvider(), array( + // format without seconds, as appears in some browsers + array('UTC', 'UTC', '2010-02-03 04:05:00 UTC', '2010-02-03T04:05Z'), + array('America/New_York', 'Asia/Hong_Kong', '2010-02-03 04:05:00 America/New_York', '2010-02-03T17:05+08:00'), + )); + } + + /** + * @dataProvider transformProvider + */ + public function testTransform($fromTz, $toTz, $from, $to) + { + $transformer = new DateTimeToRfc3339Transformer($fromTz, $toTz); + + $this->assertSame($to, $transformer->transform(null !== $from ? new \DateTime($from) : null)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testTransformRequiresValidDateTime() + { + $transformer = new DateTimeToRfc3339Transformer(); + $transformer->transform('2010-01-01'); + } + + /** + * @dataProvider reverseTransformProvider + */ + public function testReverseTransform($toTz, $fromTz, $to, $from) + { + $transformer = new DateTimeToRfc3339Transformer($toTz, $fromTz); + + if (null !== $to) { + $this->assertDateTimeEquals(new \DateTime($to), $transformer->reverseTransform($from)); + } else { + $this->assertSame($to, $transformer->reverseTransform($from)); + } + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformRequiresString() + { + $transformer = new DateTimeToRfc3339Transformer(); + $transformer->reverseTransform(12345); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithNonExistingDate() + { + $transformer = new DateTimeToRfc3339Transformer('UTC', 'UTC'); + + $transformer->reverseTransform('2010-04-31T04:05Z'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformExpectsValidDateString() + { + $transformer = new DateTimeToRfc3339Transformer('UTC', 'UTC'); + + $transformer->reverseTransform('2010-2010-2010'); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToStringTransformerTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToStringTransformerTest.php new file mode 100644 index 00000000..987df1d8 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToStringTransformerTest.php @@ -0,0 +1,181 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer; + +class DateTimeToStringTransformerTest extends DateTimeTestCase +{ + public function dataProvider() + { + $data = array( + array('Y-m-d H:i:s', '2010-02-03 16:05:06', '2010-02-03 16:05:06 UTC'), + array('Y-m-d H:i:00', '2010-02-03 16:05:00', '2010-02-03 16:05:00 UTC'), + array('Y-m-d H:i', '2010-02-03 16:05', '2010-02-03 16:05:00 UTC'), + array('Y-m-d H', '2010-02-03 16', '2010-02-03 16:00:00 UTC'), + array('Y-m-d', '2010-02-03', '2010-02-03 00:00:00 UTC'), + array('Y-m', '2010-12', '2010-12-01 00:00:00 UTC'), + array('Y', '2010', '2010-01-01 00:00:00 UTC'), + array('d-m-Y', '03-02-2010', '2010-02-03 00:00:00 UTC'), + array('H:i:s', '16:05:06', '1970-01-01 16:05:06 UTC'), + array('H:i:00', '16:05:00', '1970-01-01 16:05:00 UTC'), + array('H:i', '16:05', '1970-01-01 16:05:00 UTC'), + array('H', '16', '1970-01-01 16:00:00 UTC'), + + // different day representations + array('Y-m-j', '2010-02-3', '2010-02-03 00:00:00 UTC'), + array('z', '33', '1970-02-03 00:00:00 UTC'), + + // not bijective + // this will not work as php will use actual date to replace missing info + // and after change of date will lookup for closest Wednesday + // i.e. value: 2010-02, php value: 2010-02-(today i.e. 20), parsed date: 2010-02-24 + //array('Y-m-D', '2010-02-Wed', '2010-02-03 00:00:00 UTC'), + //array('Y-m-l', '2010-02-Wednesday', '2010-02-03 00:00:00 UTC'), + + // different month representations + array('Y-n-d', '2010-2-03', '2010-02-03 00:00:00 UTC'), + array('Y-M-d', '2010-Feb-03', '2010-02-03 00:00:00 UTC'), + array('Y-F-d', '2010-February-03', '2010-02-03 00:00:00 UTC'), + + // different year representations + array('y-m-d', '10-02-03', '2010-02-03 00:00:00 UTC'), + + // different time representations + array('G:i:s', '16:05:06', '1970-01-01 16:05:06 UTC'), + array('g:i:s a', '4:05:06 pm', '1970-01-01 16:05:06 UTC'), + array('h:i:s a', '04:05:06 pm', '1970-01-01 16:05:06 UTC'), + + // seconds since unix + array('U', '1265213106', '2010-02-03 16:05:06 UTC'), + ); + + // This test will fail < 5.3.9 - see https://bugs.php.net/51994 + if (version_compare(phpversion(), '5.3.9', '>=')) { + $data[] = array('Y-z', '2010-33', '2010-02-03 00:00:00 UTC'); + } + + return $data; + } + + /** + * @dataProvider dataProvider + */ + public function testTransform($format, $output, $input) + { + $transformer = new DateTimeToStringTransformer('UTC', 'UTC', $format); + + $input = new \DateTime($input); + + $this->assertEquals($output, $transformer->transform($input)); + } + + public function testTransformEmpty() + { + $transformer = new DateTimeToStringTransformer(); + + $this->assertSame('', $transformer->transform(null)); + } + + public function testTransformWithDifferentTimezones() + { + $transformer = new DateTimeToStringTransformer('Asia/Hong_Kong', 'America/New_York', 'Y-m-d H:i:s'); + + $input = new \DateTime('2010-02-03 12:05:06 America/New_York'); + $output = $input->format('Y-m-d H:i:s'); + $input->setTimezone(new \DateTimeZone('Asia/Hong_Kong')); + + $this->assertEquals($output, $transformer->transform($input)); + } + + public function testTransformExpectsDateTime() + { + $transformer = new DateTimeToStringTransformer(); + + $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException'); + + $transformer->transform('1234'); + } + + /** + * @dataProvider dataProvider + */ + public function testReverseTransformUsingPipe($format, $input, $output) + { + if (version_compare(phpversion(), '5.3.7', '<')) { + $this->markTestSkipped('Pipe usage requires PHP 5.3.7 or newer.'); + } + + $reverseTransformer = new DateTimeToStringTransformer('UTC', 'UTC', $format, true); + + $output = new \DateTime($output); + + $this->assertDateTimeEquals($output, $reverseTransformer->reverseTransform($input)); + } + + /** + * @dataProvider dataProvider + */ + public function testReverseTransformWithoutUsingPipe($format, $input, $output) + { + $reverseTransformer = new DateTimeToStringTransformer('UTC', 'UTC', $format, false); + + $output = new \DateTime($output); + + $this->assertDateTimeEquals($output, $reverseTransformer->reverseTransform($input)); + } + + public function testReverseTransformEmpty() + { + $reverseTransformer = new DateTimeToStringTransformer(); + + $this->assertNull($reverseTransformer->reverseTransform('')); + } + + public function testReverseTransformWithDifferentTimezones() + { + $reverseTransformer = new DateTimeToStringTransformer('America/New_York', 'Asia/Hong_Kong', 'Y-m-d H:i:s'); + + $output = new \DateTime('2010-02-03 16:05:06 Asia/Hong_Kong'); + $input = $output->format('Y-m-d H:i:s'); + $output->setTimeZone(new \DateTimeZone('America/New_York')); + + $this->assertDateTimeEquals($output, $reverseTransformer->reverseTransform($input)); + } + + public function testReverseTransformExpectsString() + { + $reverseTransformer = new DateTimeToStringTransformer(); + + $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException'); + + $reverseTransformer->reverseTransform(1234); + } + + public function testReverseTransformExpectsValidDateString() + { + $reverseTransformer = new DateTimeToStringTransformer(); + + $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException'); + + $reverseTransformer->reverseTransform('2010-2010-2010'); + } + + public function testReverseTransformWithNonExistingDate() + { + $reverseTransformer = new DateTimeToStringTransformer(); + + $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException'); + + $reverseTransformer->reverseTransform('2010-04-31'); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToTimestampTransformerTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToTimestampTransformerTest.php new file mode 100644 index 00000000..b54f0c4c --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToTimestampTransformerTest.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer; + +class DateTimeToTimestampTransformerTest extends DateTimeTestCase +{ + public function testTransform() + { + $transformer = new DateTimeToTimestampTransformer('UTC', 'UTC'); + + $input = new \DateTime('2010-02-03 04:05:06 UTC'); + $output = $input->format('U'); + + $this->assertEquals($output, $transformer->transform($input)); + } + + public function testTransformEmpty() + { + $transformer = new DateTimeToTimestampTransformer(); + + $this->assertNull($transformer->transform(null)); + } + + public function testTransformWithDifferentTimezones() + { + $transformer = new DateTimeToTimestampTransformer('Asia/Hong_Kong', 'America/New_York'); + + $input = new \DateTime('2010-02-03 04:05:06 America/New_York'); + $output = $input->format('U'); + $input->setTimezone(new \DateTimeZone('Asia/Hong_Kong')); + + $this->assertEquals($output, $transformer->transform($input)); + } + + public function testTransformFromDifferentTimezone() + { + $transformer = new DateTimeToTimestampTransformer('Asia/Hong_Kong', 'UTC'); + + $input = new \DateTime('2010-02-03 04:05:06 Asia/Hong_Kong'); + + $dateTime = clone $input; + $dateTime->setTimezone(new \DateTimeZone('UTC')); + $output = $dateTime->format('U'); + + $this->assertEquals($output, $transformer->transform($input)); + } + + public function testTransformExpectsDateTime() + { + $transformer = new DateTimeToTimestampTransformer(); + + $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException'); + + $transformer->transform('1234'); + } + + public function testReverseTransform() + { + $reverseTransformer = new DateTimeToTimestampTransformer('UTC', 'UTC'); + + $output = new \DateTime('2010-02-03 04:05:06 UTC'); + $input = $output->format('U'); + + $this->assertDateTimeEquals($output, $reverseTransformer->reverseTransform($input)); + } + + public function testReverseTransformEmpty() + { + $reverseTransformer = new DateTimeToTimestampTransformer(); + + $this->assertNull($reverseTransformer->reverseTransform(null)); + } + + public function testReverseTransformWithDifferentTimezones() + { + $reverseTransformer = new DateTimeToTimestampTransformer('Asia/Hong_Kong', 'America/New_York'); + + $output = new \DateTime('2010-02-03 04:05:06 America/New_York'); + $input = $output->format('U'); + $output->setTimezone(new \DateTimeZone('Asia/Hong_Kong')); + + $this->assertDateTimeEquals($output, $reverseTransformer->reverseTransform($input)); + } + + public function testReverseTransformExpectsValidTimestamp() + { + $reverseTransformer = new DateTimeToTimestampTransformer(); + + $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException'); + + $reverseTransformer->reverseTransform('2010-2010-2010'); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php new file mode 100644 index 00000000..a90fa91b --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\IntegerToLocalizedStringTransformer; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class IntegerToLocalizedStringTransformerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + parent::setUp(); + + // Since we test against "de_AT", we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + } + + public function testReverseTransform() + { + $transformer = new IntegerToLocalizedStringTransformer(); + + $this->assertEquals(1, $transformer->reverseTransform('1')); + $this->assertEquals(1, $transformer->reverseTransform('1,5')); + $this->assertEquals(1234, $transformer->reverseTransform('1234,5')); + $this->assertEquals(12345, $transformer->reverseTransform('12345,912')); + } + + public function testReverseTransformEmpty() + { + $transformer = new IntegerToLocalizedStringTransformer(); + + $this->assertNull($transformer->reverseTransform('')); + } + + public function testReverseTransformWithGrouping() + { + $transformer = new IntegerToLocalizedStringTransformer(null, true); + + $this->assertEquals(1234, $transformer->reverseTransform('1.234,5')); + $this->assertEquals(12345, $transformer->reverseTransform('12.345,912')); + $this->assertEquals(1234, $transformer->reverseTransform('1234,5')); + $this->assertEquals(12345, $transformer->reverseTransform('12345,912')); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformExpectsString() + { + $transformer = new IntegerToLocalizedStringTransformer(); + + $transformer->reverseTransform(1); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformExpectsValidNumber() + { + $transformer = new IntegerToLocalizedStringTransformer(); + + $transformer->reverseTransform('foo'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformDisallowsNaN() + { + $transformer = new IntegerToLocalizedStringTransformer(); + + $transformer->reverseTransform('NaN'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformDisallowsNaN2() + { + $transformer = new IntegerToLocalizedStringTransformer(); + + $transformer->reverseTransform('nan'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformDisallowsInfinity() + { + $transformer = new IntegerToLocalizedStringTransformer(); + + $transformer->reverseTransform('∞'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformDisallowsNegativeInfinity() + { + $transformer = new IntegerToLocalizedStringTransformer(); + + $transformer->reverseTransform('-∞'); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php new file mode 100644 index 00000000..8b91fe10 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\MoneyToLocalizedStringTransformer; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class MoneyToLocalizedStringTransformerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + parent::setUp(); + + // Since we test against "de_AT", we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + } + + public function testTransform() + { + $transformer = new MoneyToLocalizedStringTransformer(null, null, null, 100); + + $this->assertEquals('1,23', $transformer->transform(123)); + } + + public function testTransformExpectsNumeric() + { + $transformer = new MoneyToLocalizedStringTransformer(null, null, null, 100); + + $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException'); + + $transformer->transform('abcd'); + } + + public function testTransformEmpty() + { + $transformer = new MoneyToLocalizedStringTransformer(); + + $this->assertSame('', $transformer->transform(null)); + } + + public function testReverseTransform() + { + $transformer = new MoneyToLocalizedStringTransformer(null, null, null, 100); + + $this->assertEquals(123, $transformer->reverseTransform('1,23')); + } + + public function testReverseTransformExpectsString() + { + $transformer = new MoneyToLocalizedStringTransformer(null, null, null, 100); + + $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException'); + + $transformer->reverseTransform(12345); + } + + public function testReverseTransformEmpty() + { + $transformer = new MoneyToLocalizedStringTransformer(); + + $this->assertNull($transformer->reverseTransform('')); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php new file mode 100644 index 00000000..c58e3f60 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php @@ -0,0 +1,393 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\NumberToLocalizedStringTransformer; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class NumberToLocalizedStringTransformerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + parent::setUp(); + + // Since we test against "de_AT", we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + } + + public function provideTransformations() + { + return array( + array(null, '', 'de_AT'), + array(1, '1', 'de_AT'), + array(1.5, '1,5', 'de_AT'), + array(1234.5, '1234,5', 'de_AT'), + array(12345.912, '12345,912', 'de_AT'), + array(1234.5, '1234,5', 'ru'), + array(1234.5, '1234,5', 'fi'), + ); + } + + /** + * @dataProvider provideTransformations + */ + public function testTransform($from, $to, $locale) + { + \Locale::setDefault($locale); + + $transformer = new NumberToLocalizedStringTransformer(); + + $this->assertSame($to, $transformer->transform($from)); + } + + public function provideTransformationsWithGrouping() + { + return array( + array(1234.5, '1.234,5', 'de_AT'), + array(12345.912, '12.345,912', 'de_AT'), + array(1234.5, '1 234,5', 'fr'), + array(1234.5, '1 234,5', 'ru'), + array(1234.5, '1 234,5', 'fi'), + ); + } + + /** + * @dataProvider provideTransformationsWithGrouping + */ + public function testTransformWithGrouping($from, $to, $locale) + { + \Locale::setDefault($locale); + + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $this->assertSame($to, $transformer->transform($from)); + } + + public function testTransformWithPrecision() + { + $transformer = new NumberToLocalizedStringTransformer(2); + + $this->assertEquals('1234,50', $transformer->transform(1234.5)); + $this->assertEquals('678,92', $transformer->transform(678.916)); + } + + public function testTransformWithRoundingMode() + { + $transformer = new NumberToLocalizedStringTransformer(null, null, NumberToLocalizedStringTransformer::ROUND_DOWN); + $this->assertEquals('1234,547', $transformer->transform(1234.547), '->transform() only applies rounding mode if precision set'); + + $transformer = new NumberToLocalizedStringTransformer(2, null, NumberToLocalizedStringTransformer::ROUND_DOWN); + $this->assertEquals('1234,54', $transformer->transform(1234.547), '->transform() rounding-mode works'); + + } + + /** + * @dataProvider provideTransformations + */ + public function testReverseTransform($to, $from, $locale) + { + \Locale::setDefault($locale); + + $transformer = new NumberToLocalizedStringTransformer(); + + $this->assertEquals($to, $transformer->reverseTransform($from)); + } + + /** + * @dataProvider provideTransformationsWithGrouping + */ + public function testReverseTransformWithGrouping($to, $from, $locale) + { + \Locale::setDefault($locale); + + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $this->assertEquals($to, $transformer->reverseTransform($from)); + } + + // https://github.com/symfony/symfony/issues/7609 + public function testReverseTransformWithGroupingAndFixedSpaces() + { + if (!extension_loaded('mbstring')) { + $this->markTestSkipped('The "mbstring" extension is required for this test.'); + } + + \Locale::setDefault('ru'); + + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $this->assertEquals(1234.5, $transformer->reverseTransform("1\xc2\xa0234,5")); + } + + public function testReverseTransformWithGroupingButWithoutGroupSeparator() + { + $transformer = new NumberToLocalizedStringTransformer(null, true); + + // omit group separator + $this->assertEquals(1234.5, $transformer->reverseTransform('1234,5')); + $this->assertEquals(12345.912, $transformer->reverseTransform('12345,912')); + } + + public function testDecimalSeparatorMayBeDotIfGroupingSeparatorIsNotDot() + { + \Locale::setDefault('fr'); + $transformer = new NumberToLocalizedStringTransformer(null, true); + + // completely valid format + $this->assertEquals(1234.5, $transformer->reverseTransform('1 234,5')); + // accept dots + $this->assertEquals(1234.5, $transformer->reverseTransform('1 234.5')); + // omit group separator + $this->assertEquals(1234.5, $transformer->reverseTransform('1234,5')); + $this->assertEquals(1234.5, $transformer->reverseTransform('1234.5')); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testDecimalSeparatorMayNotBeDotIfGroupingSeparatorIsDot() + { + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $transformer->reverseTransform('1.234.5'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testDecimalSeparatorMayNotBeDotIfGroupingSeparatorIsDotWithNoGroupSep() + { + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $transformer->reverseTransform('1234.5'); + } + + public function testDecimalSeparatorMayBeDotIfGroupingSeparatorIsDotButNoGroupingUsed() + { + \Locale::setDefault('fr'); + $transformer = new NumberToLocalizedStringTransformer(); + + $this->assertEquals(1234.5, $transformer->reverseTransform('1234,5')); + $this->assertEquals(1234.5, $transformer->reverseTransform('1234.5')); + } + + public function testDecimalSeparatorMayBeCommaIfGroupingSeparatorIsNotComma() + { + \Locale::setDefault('bg'); + $transformer = new NumberToLocalizedStringTransformer(null, true); + + // completely valid format + $this->assertEquals(1234.5, $transformer->reverseTransform('1 234.5')); + // accept commas + $this->assertEquals(1234.5, $transformer->reverseTransform('1 234,5')); + // omit group separator + $this->assertEquals(1234.5, $transformer->reverseTransform('1234.5')); + $this->assertEquals(1234.5, $transformer->reverseTransform('1234,5')); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testDecimalSeparatorMayNotBeCommaIfGroupingSeparatorIsComma() + { + \Locale::setDefault('en'); + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $transformer->reverseTransform('1,234,5'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testDecimalSeparatorMayNotBeCommaIfGroupingSeparatorIsCommaWithNoGroupSep() + { + \Locale::setDefault('en'); + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $transformer->reverseTransform('1234,5'); + } + + public function testDecimalSeparatorMayBeCommaIfGroupingSeparatorIsCommaButNoGroupingUsed() + { + \Locale::setDefault('en'); + $transformer = new NumberToLocalizedStringTransformer(); + + $this->assertEquals(1234.5, $transformer->reverseTransform('1234,5')); + $this->assertEquals(1234.5, $transformer->reverseTransform('1234.5')); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testTransformExpectsNumeric() + { + $transformer = new NumberToLocalizedStringTransformer(); + + $transformer->transform('foo'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformExpectsString() + { + $transformer = new NumberToLocalizedStringTransformer(); + + $transformer->reverseTransform(1); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformExpectsValidNumber() + { + $transformer = new NumberToLocalizedStringTransformer(); + + $transformer->reverseTransform('foo'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + * @link https://github.com/symfony/symfony/issues/3161 + */ + public function testReverseTransformDisallowsNaN() + { + $transformer = new NumberToLocalizedStringTransformer(); + + $transformer->reverseTransform('NaN'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformDisallowsNaN2() + { + $transformer = new NumberToLocalizedStringTransformer(); + + $transformer->reverseTransform('nan'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformDisallowsInfinity() + { + $transformer = new NumberToLocalizedStringTransformer(); + + $transformer->reverseTransform('∞'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformDisallowsInfinity2() + { + $transformer = new NumberToLocalizedStringTransformer(); + + $transformer->reverseTransform('∞,123'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformDisallowsNegativeInfinity() + { + $transformer = new NumberToLocalizedStringTransformer(); + + $transformer->reverseTransform('-∞'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformDisallowsLeadingExtraCharacters() + { + $transformer = new NumberToLocalizedStringTransformer(); + + $transformer->reverseTransform('foo123'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + * @expectedExceptionMessage The number contains unrecognized characters: "foo3" + */ + public function testReverseTransformDisallowsCenteredExtraCharacters() + { + $transformer = new NumberToLocalizedStringTransformer(); + + $transformer->reverseTransform('12foo3'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + * @expectedExceptionMessage The number contains unrecognized characters: "foo8" + */ + public function testReverseTransformDisallowsCenteredExtraCharactersMultibyte() + { + if (!extension_loaded('mbstring')) { + $this->markTestSkipped('The "mbstring" extension is required for this test.'); + } + + \Locale::setDefault('ru'); + + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $transformer->reverseTransform("12\xc2\xa0345,67foo8"); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + * @expectedExceptionMessage The number contains unrecognized characters: "foo8" + */ + public function testReverseTransformIgnoresTrailingSpacesInExceptionMessage() + { + if (!extension_loaded('mbstring')) { + $this->markTestSkipped('The "mbstring" extension is required for this test.'); + } + + \Locale::setDefault('ru'); + + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $transformer->reverseTransform("12\xc2\xa0345,67foo8 \xc2\xa0\t"); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + * @expectedExceptionMessage The number contains unrecognized characters: "foo" + */ + public function testReverseTransformDisallowsTrailingExtraCharacters() + { + $transformer = new NumberToLocalizedStringTransformer(); + + $transformer->reverseTransform('123foo'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + * @expectedExceptionMessage The number contains unrecognized characters: "foo" + */ + public function testReverseTransformDisallowsTrailingExtraCharactersMultibyte() + { + if (!extension_loaded('mbstring')) { + $this->markTestSkipped('The "mbstring" extension is required for this test.'); + } + + \Locale::setDefault('ru'); + + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $transformer->reverseTransform("12\xc2\xa0345,678foo"); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/PercentToLocalizedStringTransformerTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/PercentToLocalizedStringTransformerTest.php new file mode 100644 index 00000000..104941c9 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/PercentToLocalizedStringTransformerTest.php @@ -0,0 +1,114 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\PercentToLocalizedStringTransformer; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class PercentToLocalizedStringTransformerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + parent::setUp(); + + // Since we test against "de_AT", we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + } + + public function testTransform() + { + $transformer = new PercentToLocalizedStringTransformer(); + + $this->assertEquals('10', $transformer->transform(0.1)); + $this->assertEquals('15', $transformer->transform(0.15)); + $this->assertEquals('12', $transformer->transform(0.1234)); + $this->assertEquals('200', $transformer->transform(2)); + } + + public function testTransformEmpty() + { + $transformer = new PercentToLocalizedStringTransformer(); + + $this->assertEquals('', $transformer->transform(null)); + } + + public function testTransformWithInteger() + { + $transformer = new PercentToLocalizedStringTransformer(null, 'integer'); + + $this->assertEquals('0', $transformer->transform(0.1)); + $this->assertEquals('1', $transformer->transform(1)); + $this->assertEquals('15', $transformer->transform(15)); + $this->assertEquals('16', $transformer->transform(15.9)); + } + + public function testTransformWithPrecision() + { + $transformer = new PercentToLocalizedStringTransformer(2); + + $this->assertEquals('12,34', $transformer->transform(0.1234)); + } + + public function testReverseTransform() + { + $transformer = new PercentToLocalizedStringTransformer(); + + $this->assertEquals(0.1, $transformer->reverseTransform('10')); + $this->assertEquals(0.15, $transformer->reverseTransform('15')); + $this->assertEquals(0.12, $transformer->reverseTransform('12')); + $this->assertEquals(2, $transformer->reverseTransform('200')); + } + + public function testReverseTransformEmpty() + { + $transformer = new PercentToLocalizedStringTransformer(); + + $this->assertNull($transformer->reverseTransform('')); + } + + public function testReverseTransformWithInteger() + { + $transformer = new PercentToLocalizedStringTransformer(null, 'integer'); + + $this->assertEquals(10, $transformer->reverseTransform('10')); + $this->assertEquals(15, $transformer->reverseTransform('15')); + $this->assertEquals(12, $transformer->reverseTransform('12')); + $this->assertEquals(200, $transformer->reverseTransform('200')); + } + + public function testReverseTransformWithPrecision() + { + $transformer = new PercentToLocalizedStringTransformer(2); + + $this->assertEquals(0.1234, $transformer->reverseTransform('12,34')); + } + + public function testTransformExpectsNumeric() + { + $transformer = new PercentToLocalizedStringTransformer(); + + $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException'); + + $transformer->transform('foo'); + } + + public function testReverseTransformExpectsString() + { + $transformer = new PercentToLocalizedStringTransformer(); + + $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException'); + + $transformer->reverseTransform(1); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ValueToDuplicatesTransformerTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ValueToDuplicatesTransformerTest.php new file mode 100644 index 00000000..2c5298da --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ValueToDuplicatesTransformerTest.php @@ -0,0 +1,120 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\ValueToDuplicatesTransformer; + +class ValueToDuplicatesTransformerTest extends \PHPUnit_Framework_TestCase +{ + private $transformer; + + protected function setUp() + { + $this->transformer = new ValueToDuplicatesTransformer(array('a', 'b', 'c')); + } + + protected function tearDown() + { + $this->transformer = null; + } + + public function testTransform() + { + $output = array( + 'a' => 'Foo', + 'b' => 'Foo', + 'c' => 'Foo', + ); + + $this->assertSame($output, $this->transformer->transform('Foo')); + } + + public function testTransformEmpty() + { + $output = array( + 'a' => null, + 'b' => null, + 'c' => null, + ); + + $this->assertSame($output, $this->transformer->transform(null)); + } + + public function testReverseTransform() + { + $input = array( + 'a' => 'Foo', + 'b' => 'Foo', + 'c' => 'Foo', + ); + + $this->assertSame('Foo', $this->transformer->reverseTransform($input)); + } + + public function testReverseTransformCompletelyEmpty() + { + $input = array( + 'a' => '', + 'b' => '', + 'c' => '', + ); + + $this->assertNull($this->transformer->reverseTransform($input)); + } + + public function testReverseTransformCompletelyNull() + { + $input = array( + 'a' => null, + 'b' => null, + 'c' => null, + ); + + $this->assertNull($this->transformer->reverseTransform($input)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformPartiallyNull() + { + $input = array( + 'a' => 'Foo', + 'b' => 'Foo', + 'c' => null, + ); + + $this->transformer->reverseTransform($input); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformDifferences() + { + $input = array( + 'a' => 'Foo', + 'b' => 'Bar', + 'c' => 'Foo', + ); + + $this->transformer->reverseTransform($input); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformRequiresArray() + { + $this->transformer->reverseTransform('12345'); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixRadioInputListenerTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixRadioInputListenerTest.php new file mode 100644 index 00000000..a5d5c78a --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixRadioInputListenerTest.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\EventListener; + +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Extension\Core\EventListener\FixRadioInputListener; +use Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList; + +class FixRadioInputListenerTest extends \PHPUnit_Framework_TestCase +{ + private $choiceList; + + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + parent::setUp(); + + $this->choiceList = new SimpleChoiceList(array('' => 'Empty', 0 => 'A', 1 => 'B')); + } + + protected function tearDown() + { + parent::tearDown(); + + $listener = null; + } + + public function testFixRadio() + { + $data = '1'; + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $event = new FormEvent($form, $data); + + $listener = new FixRadioInputListener($this->choiceList, true); + $listener->preSubmit($event); + + // Indices in SimpleChoiceList are zero-based generated integers + $this->assertEquals(array(2 => '1'), $event->getData()); + } + + public function testFixZero() + { + $data = '0'; + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $event = new FormEvent($form, $data); + + $listener = new FixRadioInputListener($this->choiceList, true); + $listener->preSubmit($event); + + // Indices in SimpleChoiceList are zero-based generated integers + $this->assertEquals(array(1 => '0'), $event->getData()); + } + + public function testFixEmptyString() + { + $data = ''; + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $event = new FormEvent($form, $data); + + $listener = new FixRadioInputListener($this->choiceList, true); + $listener->preSubmit($event); + + // Indices in SimpleChoiceList are zero-based generated integers + $this->assertEquals(array(0 => ''), $event->getData()); + } + + public function testConvertEmptyStringToPlaceholderIfNotFound() + { + $list = new SimpleChoiceList(array(0 => 'A', 1 => 'B')); + + $data = ''; + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $event = new FormEvent($form, $data); + + $listener = new FixRadioInputListener($list, true); + $listener->preSubmit($event); + + $this->assertEquals(array('placeholder' => ''), $event->getData()); + } + + public function testDontConvertEmptyStringToPlaceholderIfNoPlaceholderUsed() + { + $list = new SimpleChoiceList(array(0 => 'A', 1 => 'B')); + + $data = ''; + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $event = new FormEvent($form, $data); + + $listener = new FixRadioInputListener($list, false); + $listener->preSubmit($event); + + $this->assertEquals(array(), $event->getData()); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixUrlProtocolListenerTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixUrlProtocolListenerTest.php new file mode 100644 index 00000000..2b84e4fd --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixUrlProtocolListenerTest.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\EventListener; + +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Extension\Core\EventListener\FixUrlProtocolListener; + +class FixUrlProtocolListenerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + } + + public function testFixHttpUrl() + { + $data = "www.symfony.com"; + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $event = new FormEvent($form, $data); + + $filter = new FixUrlProtocolListener('http'); + $filter->onSubmit($event); + + $this->assertEquals('http://www.symfony.com', $event->getData()); + } + + public function testSkipKnownUrl() + { + $data = "http://www.symfony.com"; + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $event = new FormEvent($form, $data); + + $filter = new FixUrlProtocolListener('http'); + $filter->onSubmit($event); + + $this->assertEquals('http://www.symfony.com', $event->getData()); + } + + public function testSkipOtherProtocol() + { + $data = "ftp://www.symfony.com"; + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $event = new FormEvent($form, $data); + + $filter = new FixUrlProtocolListener('http'); + $filter->onSubmit($event); + + $this->assertEquals('ftp://www.symfony.com', $event->getData()); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/Fixtures/randomhash b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/Fixtures/randomhash new file mode 100644 index 00000000..b636f4b8 Binary files /dev/null and b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/Fixtures/randomhash differ diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayObjectTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayObjectTest.php new file mode 100644 index 00000000..6f46c9d7 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayObjectTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\EventListener; + +use Symfony\Component\Form\FormBuilder; + +class MergeCollectionListenerArrayObjectTest extends MergeCollectionListenerTest +{ + protected function getData(array $data) + { + return new \ArrayObject($data); + } + + protected function getBuilder($name = 'name') + { + return new FormBuilder($name, '\ArrayObject', $this->dispatcher, $this->factory); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayTest.php new file mode 100644 index 00000000..c0f3d597 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\EventListener; + +use Symfony\Component\Form\FormBuilder; + +class MergeCollectionListenerArrayTest extends MergeCollectionListenerTest +{ + protected function getData(array $data) + { + return $data; + } + + protected function getBuilder($name = 'name') + { + return new FormBuilder($name, null, $this->dispatcher, $this->factory); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerCustomArrayObjectTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerCustomArrayObjectTest.php new file mode 100644 index 00000000..5eb6c7b9 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerCustomArrayObjectTest.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\EventListener; + +use Symfony\Component\Form\Tests\Fixtures\CustomArrayObject; +use Symfony\Component\Form\FormBuilder; + +class MergeCollectionListenerCustomArrayObjectTest extends MergeCollectionListenerTest +{ + protected function getData(array $data) + { + return new CustomArrayObject($data); + } + + protected function getBuilder($name = 'name') + { + return new FormBuilder($name, 'Symfony\Component\Form\Tests\Fixtures\CustomArrayObject', $this->dispatcher, $this->factory); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerTest.php new file mode 100644 index 00000000..dbd28c6b --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerTest.php @@ -0,0 +1,259 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\EventListener; + +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Extension\Core\EventListener\MergeCollectionListener; + +abstract class MergeCollectionListenerTest extends \PHPUnit_Framework_TestCase +{ + protected $dispatcher; + protected $factory; + protected $form; + + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); + $this->form = $this->getForm('axes'); + } + + protected function tearDown() + { + $this->dispatcher = null; + $this->factory = null; + $this->form = null; + } + + abstract protected function getBuilder($name = 'name'); + + protected function getForm($name = 'name', $propertyPath = null) + { + $propertyPath = $propertyPath ?: $name; + + return $this->getBuilder($name)->setAttribute('property_path', $propertyPath)->getForm(); + } + + protected function getMockForm() + { + return $this->getMock('Symfony\Component\Form\Test\FormInterface'); + } + + public function getBooleanMatrix1() + { + return array( + array(true), + array(false), + ); + } + + public function getBooleanMatrix2() + { + return array( + array(true, true), + array(true, false), + array(false, true), + array(false, false), + ); + } + + abstract protected function getData(array $data); + + /** + * @dataProvider getBooleanMatrix1 + */ + public function testAddExtraEntriesIfAllowAdd($allowDelete) + { + $originalData = $this->getData(array(1 => 'second')); + $newData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third')); + + $listener = new MergeCollectionListener(true, $allowDelete); + + $this->form->setData($originalData); + + $event = new FormEvent($this->form, $newData); + $listener->onSubmit($event); + + // The original object was modified + if (is_object($originalData)) { + $this->assertSame($originalData, $event->getData()); + } + + // The original object matches the new object + $this->assertEquals($newData, $event->getData()); + } + + /** + * @dataProvider getBooleanMatrix1 + */ + public function testAddExtraEntriesIfAllowAddDontOverwriteExistingIndices($allowDelete) + { + $originalData = $this->getData(array(1 => 'first')); + $newData = $this->getData(array(0 => 'first', 1 => 'second')); + + $listener = new MergeCollectionListener(true, $allowDelete); + + $this->form->setData($originalData); + + $event = new FormEvent($this->form, $newData); + $listener->onSubmit($event); + + // The original object was modified + if (is_object($originalData)) { + $this->assertSame($originalData, $event->getData()); + } + + // The original object matches the new object + $this->assertEquals($this->getData(array(1 => 'first', 2 => 'second')), $event->getData()); + } + + /** + * @dataProvider getBooleanMatrix1 + */ + public function testDoNothingIfNotAllowAdd($allowDelete) + { + $originalDataArray = array(1 => 'second'); + $originalData = $this->getData($originalDataArray); + $newData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third')); + + $listener = new MergeCollectionListener(false, $allowDelete); + + $this->form->setData($originalData); + + $event = new FormEvent($this->form, $newData); + $listener->onSubmit($event); + + // We still have the original object + if (is_object($originalData)) { + $this->assertSame($originalData, $event->getData()); + } + + // Nothing was removed + $this->assertEquals($this->getData($originalDataArray), $event->getData()); + } + + /** + * @dataProvider getBooleanMatrix1 + */ + public function testRemoveMissingEntriesIfAllowDelete($allowAdd) + { + $originalData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third')); + $newData = $this->getData(array(1 => 'second')); + + $listener = new MergeCollectionListener($allowAdd, true); + + $this->form->setData($originalData); + + $event = new FormEvent($this->form, $newData); + $listener->onSubmit($event); + + // The original object was modified + if (is_object($originalData)) { + $this->assertSame($originalData, $event->getData()); + } + + // The original object matches the new object + $this->assertEquals($newData, $event->getData()); + } + + /** + * @dataProvider getBooleanMatrix1 + */ + public function testDoNothingIfNotAllowDelete($allowAdd) + { + $originalDataArray = array(0 => 'first', 1 => 'second', 2 => 'third'); + $originalData = $this->getData($originalDataArray); + $newData = $this->getData(array(1 => 'second')); + + $listener = new MergeCollectionListener($allowAdd, false); + + $this->form->setData($originalData); + + $event = new FormEvent($this->form, $newData); + $listener->onSubmit($event); + + // We still have the original object + if (is_object($originalData)) { + $this->assertSame($originalData, $event->getData()); + } + + // Nothing was removed + $this->assertEquals($this->getData($originalDataArray), $event->getData()); + } + + /** + * @dataProvider getBooleanMatrix2 + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + */ + public function testRequireArrayOrTraversable($allowAdd, $allowDelete) + { + $newData = 'no array or traversable'; + $event = new FormEvent($this->form, $newData); + $listener = new MergeCollectionListener($allowAdd, $allowDelete); + $listener->onSubmit($event); + } + + public function testDealWithNullData() + { + $originalData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third')); + $newData = null; + + $listener = new MergeCollectionListener(false, false); + + $this->form->setData($originalData); + + $event = new FormEvent($this->form, $newData); + $listener->onSubmit($event); + + $this->assertSame($originalData, $event->getData()); + } + + /** + * @dataProvider getBooleanMatrix1 + */ + public function testDealWithNullOriginalDataIfAllowAdd($allowDelete) + { + $originalData = null; + $newData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third')); + + $listener = new MergeCollectionListener(true, $allowDelete); + + $this->form->setData($originalData); + + $event = new FormEvent($this->form, $newData); + $listener->onSubmit($event); + + $this->assertSame($newData, $event->getData()); + } + + /** + * @dataProvider getBooleanMatrix1 + */ + public function testDontDealWithNullOriginalDataIfNotAllowAdd($allowDelete) + { + $originalData = null; + $newData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third')); + + $listener = new MergeCollectionListener(false, $allowDelete); + + $this->form->setData($originalData); + + $event = new FormEvent($this->form, $newData); + $listener->onSubmit($event); + + $this->assertNull($event->getData()); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/ResizeFormListenerTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/ResizeFormListenerTest.php new file mode 100644 index 00000000..1367b3ef --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/ResizeFormListenerTest.php @@ -0,0 +1,255 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\EventListener; + +use Symfony\Component\Form\Extension\Core\EventListener\ResizeFormListener; +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Form\FormEvent; + +class ResizeFormListenerTest extends \PHPUnit_Framework_TestCase +{ + private $dispatcher; + private $factory; + private $form; + + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); + $this->form = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + } + + protected function tearDown() + { + $this->dispatcher = null; + $this->factory = null; + $this->form = null; + } + + protected function getBuilder($name = 'name') + { + return new FormBuilder($name, null, $this->dispatcher, $this->factory); + } + + protected function getForm($name = 'name') + { + return $this->getBuilder($name)->getForm(); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getDataMapper() + { + return $this->getMock('Symfony\Component\Form\DataMapperInterface'); + } + + protected function getMockForm() + { + return $this->getMock('Symfony\Component\Form\Test\FormInterface'); + } + + public function testPreSetDataResizesForm() + { + $this->form->add($this->getForm('0')); + $this->form->add($this->getForm('1')); + + $this->factory->expects($this->at(0)) + ->method('createNamed') + ->with(1, 'text', null, array('property_path' => '[1]', 'max_length' => 10, 'auto_initialize' => false)) + ->will($this->returnValue($this->getForm('1'))); + $this->factory->expects($this->at(1)) + ->method('createNamed') + ->with(2, 'text', null, array('property_path' => '[2]', 'max_length' => 10, 'auto_initialize' => false)) + ->will($this->returnValue($this->getForm('2'))); + + $data = array(1 => 'string', 2 => 'string'); + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array('max_length' => '10'), false, false); + $listener->preSetData($event); + + $this->assertFalse($this->form->has('0')); + $this->assertTrue($this->form->has('1')); + $this->assertTrue($this->form->has('2')); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + */ + public function testPreSetDataRequiresArrayOrTraversable() + { + $data = 'no array or traversable'; + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, false); + $listener->preSetData($event); + } + + public function testPreSetDataDealsWithNullData() + { + $this->factory->expects($this->never())->method('createNamed'); + + $data = null; + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, false); + $listener->preSetData($event); + } + + public function testPreSubmitResizesUpIfAllowAdd() + { + $this->form->add($this->getForm('0')); + + $this->factory->expects($this->once()) + ->method('createNamed') + ->with(1, 'text', null, array('property_path' => '[1]', 'max_length' => 10, 'auto_initialize' => false)) + ->will($this->returnValue($this->getForm('1'))); + + $data = array(0 => 'string', 1 => 'string'); + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array('max_length' => 10), true, false); + $listener->preSubmit($event); + + $this->assertTrue($this->form->has('0')); + $this->assertTrue($this->form->has('1')); + } + + public function testPreSubmitResizesDownIfAllowDelete() + { + $this->form->add($this->getForm('0')); + $this->form->add($this->getForm('1')); + + $data = array(0 => 'string'); + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, true); + $listener->preSubmit($event); + + $this->assertTrue($this->form->has('0')); + $this->assertFalse($this->form->has('1')); + } + + // fix for https://github.com/symfony/symfony/pull/493 + public function testPreSubmitRemovesZeroKeys() + { + $this->form->add($this->getForm('0')); + + $data = array(); + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, true); + $listener->preSubmit($event); + + $this->assertFalse($this->form->has('0')); + } + + public function testPreSubmitDoesNothingIfNotAllowAddNorAllowDelete() + { + $this->form->add($this->getForm('0')); + $this->form->add($this->getForm('1')); + + $data = array(0 => 'string', 2 => 'string'); + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, false); + $listener->preSubmit($event); + + $this->assertTrue($this->form->has('0')); + $this->assertTrue($this->form->has('1')); + $this->assertFalse($this->form->has('2')); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + */ + public function testPreSubmitRequiresArrayOrTraversable() + { + $data = 'no array or traversable'; + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, false); + $listener->preSubmit($event); + } + + public function testPreSubmitDealsWithNullData() + { + $this->form->add($this->getForm('1')); + + $data = null; + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, true); + $listener->preSubmit($event); + + $this->assertFalse($this->form->has('1')); + } + + // fixes https://github.com/symfony/symfony/pull/40 + public function testPreSubmitDealsWithEmptyData() + { + $this->form->add($this->getForm('1')); + + $data = ''; + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, true); + $listener->preSubmit($event); + + $this->assertFalse($this->form->has('1')); + } + + public function testOnSubmitNormDataRemovesEntriesMissingInTheFormIfAllowDelete() + { + $this->form->add($this->getForm('1')); + + $data = array(0 => 'first', 1 => 'second', 2 => 'third'); + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, true); + $listener->onSubmit($event); + + $this->assertEquals(array(1 => 'second'), $event->getData()); + } + + public function testOnSubmitNormDataDoesNothingIfNotAllowDelete() + { + $this->form->add($this->getForm('1')); + + $data = array(0 => 'first', 1 => 'second', 2 => 'third'); + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, false); + $listener->onSubmit($event); + + $this->assertEquals($data, $event->getData()); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + */ + public function testOnSubmitNormDataRequiresArrayOrTraversable() + { + $data = 'no array or traversable'; + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, false); + $listener->onSubmit($event); + } + + public function testOnSubmitNormDataDealsWithNullData() + { + $this->form->add($this->getForm('1')); + + $data = null; + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, true); + $listener->onSubmit($event); + + $this->assertEquals(array(), $event->getData()); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/TrimListenerTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/TrimListenerTest.php new file mode 100644 index 00000000..4e368933 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/TrimListenerTest.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\EventListener; + +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Extension\Core\EventListener\TrimListener; + +class TrimListenerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + } + + public function testTrim() + { + $data = " Foo! "; + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $event = new FormEvent($form, $data); + + $filter = new TrimListener(); + $filter->preSubmit($event); + + $this->assertEquals('Foo!', $event->getData()); + } + + public function testTrimSkipNonStrings() + { + $data = 1234; + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $event = new FormEvent($form, $data); + + $filter = new TrimListener(); + $filter->preSubmit($event); + + $this->assertSame(1234, $event->getData()); + } + + /** + * @dataProvider codePointProvider + */ + public function testTrimUtf8($chars) + { + if (!function_exists('mb_check_encoding')) { + $this->markTestSkipped('The "mb_check_encoding" function is not available'); + } + + $data = mb_convert_encoding(pack('H*', implode('', $chars)), 'UTF-8', 'UCS-2BE'); + $data = $data."ab\ncd".$data; + + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $event = new FormEvent($form, $data); + + $filter = new TrimListener(); + $filter->preSubmit($event); + + $this->assertSame("ab\ncd", $event->getData(), 'TrimListener should trim character(s): '.implode(', ', $chars)); + } + + public function codePointProvider() + { + return array( + 'General category: Separator' => array(array('0020', '00A0', '1680', '180E', '2000', '2001', '2002', '2003', '2004', '2005', '2006', '2007', '2008', '2009', '200A', '2028', '2029', '202F', '205F', '3000')), + 'General category: Other, control' => array(array('0009', '000A', '000B', '000C', '000D', '0085')), + //'General category: Other, format. ZERO WIDTH SPACE' => array(array('200B')), + ); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/BaseTypeTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/BaseTypeTest.php new file mode 100644 index 00000000..bfa1e218 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/BaseTypeTest.php @@ -0,0 +1,135 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +/** + * @author Bernhard Schussek + */ +abstract class BaseTypeTest extends \Symfony\Component\Form\Test\TypeTestCase +{ + public function testPassDisabledAsOption() + { + $form = $this->factory->create($this->getTestedType(), null, array('disabled' => true)); + + $this->assertTrue($form->isDisabled()); + } + + public function testPassIdAndNameToView() + { + $view = $this->factory->createNamed('name', $this->getTestedType()) + ->createView(); + + $this->assertEquals('name', $view->vars['id']); + $this->assertEquals('name', $view->vars['name']); + $this->assertEquals('name', $view->vars['full_name']); + } + + public function testStripLeadingUnderscoresAndDigitsFromId() + { + $view = $this->factory->createNamed('_09name', $this->getTestedType()) + ->createView(); + + $this->assertEquals('name', $view->vars['id']); + $this->assertEquals('_09name', $view->vars['name']); + $this->assertEquals('_09name', $view->vars['full_name']); + } + + public function testPassIdAndNameToViewWithParent() + { + $view = $this->factory->createNamedBuilder('parent', 'form') + ->add('child', $this->getTestedType()) + ->getForm() + ->createView(); + + $this->assertEquals('parent_child', $view['child']->vars['id']); + $this->assertEquals('child', $view['child']->vars['name']); + $this->assertEquals('parent[child]', $view['child']->vars['full_name']); + } + + public function testPassIdAndNameToViewWithGrandParent() + { + $builder = $this->factory->createNamedBuilder('parent', 'form') + ->add('child', 'form'); + $builder->get('child')->add('grand_child', $this->getTestedType()); + $view = $builder->getForm()->createView(); + + $this->assertEquals('parent_child_grand_child', $view['child']['grand_child']->vars['id']); + $this->assertEquals('grand_child', $view['child']['grand_child']->vars['name']); + $this->assertEquals('parent[child][grand_child]', $view['child']['grand_child']->vars['full_name']); + } + + public function testPassTranslationDomainToView() + { + $form = $this->factory->create($this->getTestedType(), null, array( + 'translation_domain' => 'domain', + )); + $view = $form->createView(); + + $this->assertSame('domain', $view->vars['translation_domain']); + } + + public function testInheritTranslationDomainFromParent() + { + $view = $this->factory + ->createNamedBuilder('parent', 'form', null, array( + 'translation_domain' => 'domain', + )) + ->add('child', $this->getTestedType()) + ->getForm() + ->createView(); + + $this->assertEquals('domain', $view['child']->vars['translation_domain']); + } + + public function testPreferOwnTranslationDomain() + { + $view = $this->factory + ->createNamedBuilder('parent', 'form', null, array( + 'translation_domain' => 'parent_domain', + )) + ->add('child', $this->getTestedType(), array( + 'translation_domain' => 'domain', + )) + ->getForm() + ->createView(); + + $this->assertEquals('domain', $view['child']->vars['translation_domain']); + } + + public function testDefaultTranslationDomain() + { + $view = $this->factory->createNamedBuilder('parent', 'form') + ->add('child', $this->getTestedType()) + ->getForm() + ->createView(); + + $this->assertEquals('messages', $view['child']->vars['translation_domain']); + } + + public function testPassLabelToView() + { + $form = $this->factory->createNamed('__test___field', $this->getTestedType(), null, array('label' => 'My label')); + $view = $form->createView(); + + $this->assertSame('My label', $view->vars['label']); + } + + public function testPassMultipartFalseToView() + { + $form = $this->factory->create($this->getTestedType()); + $view = $form->createView(); + + $this->assertFalse($view->vars['multipart']); + } + + abstract protected function getTestedType(); +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/ButtonTypeTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/ButtonTypeTest.php new file mode 100644 index 00000000..55835e77 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/ButtonTypeTest.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +/** + * @author Bernhard Schussek + */ +class ButtonTypeTest extends BaseTypeTest +{ + public function testCreateButtonInstances() + { + $this->assertInstanceOf('Symfony\Component\Form\Button', $this->factory->create('button')); + } + + protected function getTestedType() + { + return 'button'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php new file mode 100644 index 00000000..c782adab --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php @@ -0,0 +1,162 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\CallbackTransformer; + +class CheckboxTypeTest extends \Symfony\Component\Form\Test\TypeTestCase +{ + public function testPassValueToView() + { + $form = $this->factory->create('checkbox', null, array('value' => 'foobar')); + $view = $form->createView(); + + $this->assertEquals('foobar', $view->vars['value']); + } + + public function testCheckedIfDataTrue() + { + $form = $this->factory->create('checkbox'); + $form->setData(true); + $view = $form->createView(); + + $this->assertTrue($view->vars['checked']); + } + + public function testCheckedIfDataTrueWithEmptyValue() + { + $form = $this->factory->create('checkbox', null, array('value' => '')); + $form->setData(true); + $view = $form->createView(); + + $this->assertTrue($view->vars['checked']); + } + + public function testNotCheckedIfDataFalse() + { + $form = $this->factory->create('checkbox'); + $form->setData(false); + $view = $form->createView(); + + $this->assertFalse($view->vars['checked']); + } + + public function testSubmitWithValueChecked() + { + $form = $this->factory->create('checkbox', null, array( + 'value' => 'foobar', + )); + $form->submit('foobar'); + + $this->assertTrue($form->getData()); + $this->assertEquals('foobar', $form->getViewData()); + } + + public function testSubmitWithRandomValueChecked() + { + $form = $this->factory->create('checkbox', null, array( + 'value' => 'foobar', + )); + $form->submit('krixikraxi'); + + $this->assertTrue($form->getData()); + $this->assertEquals('foobar', $form->getViewData()); + } + + public function testSubmitWithValueUnchecked() + { + $form = $this->factory->create('checkbox', null, array( + 'value' => 'foobar', + )); + $form->submit(null); + + $this->assertFalse($form->getData()); + $this->assertNull($form->getViewData()); + } + + public function testSubmitWithEmptyValueChecked() + { + $form = $this->factory->create('checkbox', null, array( + 'value' => '', + )); + $form->submit(''); + + $this->assertTrue($form->getData()); + $this->assertSame('', $form->getViewData()); + } + + public function testSubmitWithEmptyValueUnchecked() + { + $form = $this->factory->create('checkbox', null, array( + 'value' => '', + )); + $form->submit(null); + + $this->assertFalse($form->getData()); + $this->assertNull($form->getViewData()); + } + + public function testBindWithEmptyValueAndFalseUnchecked() + { + $form = $this->factory->create('checkbox', null, array( + 'value' => '', + )); + $form->bind(false); + + $this->assertFalse($form->getData()); + $this->assertNull($form->getViewData()); + } + + public function testBindWithEmptyValueAndTrueChecked() + { + $form = $this->factory->create('checkbox', null, array( + 'value' => '', + )); + $form->bind(true); + + $this->assertTrue($form->getData()); + $this->assertSame('', $form->getViewData()); + } + + /** + * @dataProvider provideTransformedData + */ + public function testTransformedData($data, $expected) + { + // present a binary status field as a checkbox + $transformer = new CallbackTransformer( + function ($value) { + return 'expedited' == $value; + }, + function ($value) { + return $value ? 'expedited' : 'standard'; + } + ); + + $form = $this->builder + ->create('expedited_shipping', 'checkbox') + ->addModelTransformer($transformer) + ->getForm(); + $form->setData($data); + $view = $form->createView(); + + $this->assertEquals($expected, $view->vars['checked']); + } + + public function provideTransformedData() + { + return array( + array('expedited', true), + array('standard', false), + ); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypePerformanceTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypePerformanceTest.php new file mode 100644 index 00000000..0685946f --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypePerformanceTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Test\FormPerformanceTestCase; + +/** + * @author Bernhard Schussek + */ +class ChoiceTypePerformanceTest extends FormPerformanceTestCase +{ + /** + * This test case is realistic in collection forms where each + * row contains the same choice field. + * + * @group benchmark + */ + public function testSameChoiceFieldCreatedMultipleTimes() + { + $this->setMaxRunningTime(1); + $choices = range(1, 300); + + for ($i = 0; $i < 100; ++$i) { + $this->factory->create('choice', rand(1, 400), array( + 'choices' => $choices, + )); + } + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php new file mode 100644 index 00000000..219e8181 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php @@ -0,0 +1,949 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Extension\Core\ChoiceList\ObjectChoiceList; +use Symfony\Component\Form\Extension\Core\View\ChoiceView; + +class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase +{ + private $choices = array( + 'a' => 'Bernhard', + 'b' => 'Fabien', + 'c' => 'Kris', + 'd' => 'Jon', + 'e' => 'Roman', + ); + + private $numericChoices = array( + 0 => 'Bernhard', + 1 => 'Fabien', + 2 => 'Kris', + 3 => 'Jon', + 4 => 'Roman', + ); + + private $objectChoices; + + protected $groupedChoices = array( + 'Symfony' => array( + 'a' => 'Bernhard', + 'b' => 'Fabien', + 'c' => 'Kris', + ), + 'Doctrine' => array( + 'd' => 'Jon', + 'e' => 'Roman', + ) + ); + + protected function setUp() + { + parent::setUp(); + + $this->objectChoices = array( + (object) array('id' => 1, 'name' => 'Bernhard'), + (object) array('id' => 2, 'name' => 'Fabien'), + (object) array('id' => 3, 'name' => 'Kris'), + (object) array('id' => 4, 'name' => 'Jon'), + (object) array('id' => 5, 'name' => 'Roman'), + ); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->objectChoices = null; + } + + /** + * @expectedException \PHPUnit_Framework_Error + */ + public function testChoicesOptionExpectsArray() + { + $this->factory->create('choice', null, array( + 'choices' => new \ArrayObject(), + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testChoiceListOptionExpectsChoiceListInterface() + { + $this->factory->create('choice', null, array( + 'choice_list' => array('foo' => 'foo'), + )); + } + + public function testChoiceListAndChoicesCanBeEmpty() + { + $this->factory->create('choice'); + } + + public function testExpandedChoicesOptionsTurnIntoChildren() + { + $form = $this->factory->create('choice', null, array( + 'expanded' => true, + 'choices' => $this->choices, + )); + + $this->assertCount(count($this->choices), $form, 'Each choice should become a new field'); + } + + public function testPlaceholderPresentOnNonRequiredExpandedSingleChoice() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => false, + 'choices' => $this->choices, + )); + + $this->assertTrue(isset($form['placeholder'])); + $this->assertCount(count($this->choices) + 1, $form, 'Each choice should become a new field'); + } + + public function testPlaceholderNotPresentIfRequired() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => true, + 'choices' => $this->choices, + )); + + $this->assertFalse(isset($form['placeholder'])); + $this->assertCount(count($this->choices), $form, 'Each choice should become a new field'); + } + + public function testPlaceholderNotPresentIfMultiple() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => true, + 'expanded' => true, + 'required' => false, + 'choices' => $this->choices, + )); + + $this->assertFalse(isset($form['placeholder'])); + $this->assertCount(count($this->choices), $form, 'Each choice should become a new field'); + } + + public function testPlaceholderNotPresentIfEmptyChoice() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => false, + 'choices' => array( + '' => 'Empty', + 1 => 'Not empty', + ), + )); + + $this->assertFalse(isset($form['placeholder'])); + $this->assertCount(2, $form, 'Each choice should become a new field'); + } + + public function testExpandedChoicesOptionsAreFlattened() + { + $form = $this->factory->create('choice', null, array( + 'expanded' => true, + 'choices' => $this->groupedChoices, + )); + + $flattened = array(); + foreach ($this->groupedChoices as $choices) { + $flattened = array_merge($flattened, array_keys($choices)); + } + + $this->assertCount($form->count(), $flattened, 'Each nested choice should become a new field, not the groups'); + + foreach ($flattened as $value => $choice) { + $this->assertTrue($form->has($value), 'Flattened choice is named after it\'s value'); + } + } + + public function testExpandedCheckboxesAreNeverRequired() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => true, + 'expanded' => true, + 'required' => true, + 'choices' => $this->choices, + )); + + foreach ($form as $child) { + $this->assertFalse($child->isRequired()); + } + } + + public function testExpandedRadiosAreRequiredIfChoiceChildIsRequired() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => true, + 'choices' => $this->choices, + )); + + foreach ($form as $child) { + $this->assertTrue($child->isRequired()); + } + } + + public function testExpandedRadiosAreNotRequiredIfChoiceChildIsNotRequired() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => false, + 'choices' => $this->choices, + )); + + foreach ($form as $child) { + $this->assertFalse($child->isRequired()); + } + } + + public function testSubmitSingleNonExpanded() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => false, + 'choices' => $this->choices, + )); + + $form->submit('b'); + + $this->assertEquals('b', $form->getData()); + $this->assertEquals('b', $form->getViewData()); + } + + public function testSubmitSingleNonExpandedObjectChoices() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => false, + 'choice_list' => new ObjectChoiceList( + $this->objectChoices, + // label path + 'name', + array(), + null, + // value path + 'id' + ), + )); + + // "id" value of the second entry + $form->submit('2'); + + $this->assertEquals($this->objectChoices[1], $form->getData()); + $this->assertEquals('2', $form->getViewData()); + } + + public function testSubmitMultipleNonExpanded() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => true, + 'expanded' => false, + 'choices' => $this->choices, + )); + + $form->submit(array('a', 'b')); + + $this->assertEquals(array('a', 'b'), $form->getData()); + $this->assertEquals(array('a', 'b'), $form->getViewData()); + } + + public function testSubmitMultipleNonExpandedObjectChoices() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => true, + 'expanded' => false, + 'choice_list' => new ObjectChoiceList( + $this->objectChoices, + // label path + 'name', + array(), + null, + // value path + 'id' + ), + )); + + $form->submit(array('2', '3')); + + $this->assertEquals(array($this->objectChoices[1], $this->objectChoices[2]), $form->getData()); + $this->assertEquals(array('2', '3'), $form->getViewData()); + } + + public function testSubmitSingleExpandedRequired() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => true, + 'choices' => $this->choices, + )); + + $form->submit('b'); + + $this->assertSame('b', $form->getData()); + $this->assertSame(array( + 0 => false, + 1 => true, + 2 => false, + 3 => false, + 4 => false, + ), $form->getViewData()); + + $this->assertFalse($form[0]->getData()); + $this->assertTrue($form[1]->getData()); + $this->assertFalse($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertNull($form[0]->getViewData()); + $this->assertSame('b', $form[1]->getViewData()); + $this->assertNull($form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + public function testSubmitSingleExpandedNonRequired() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => false, + 'choices' => $this->choices, + )); + + $form->submit('b'); + + $this->assertSame('b', $form->getData()); + $this->assertSame(array( + 0 => false, + 1 => true, + 2 => false, + 3 => false, + 4 => false, + 'placeholder' => false, + ), $form->getViewData()); + + $this->assertFalse($form['placeholder']->getData()); + $this->assertFalse($form[0]->getData()); + $this->assertTrue($form[1]->getData()); + $this->assertFalse($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertNull($form['placeholder']->getViewData()); + $this->assertNull($form[0]->getViewData()); + $this->assertSame('b', $form[1]->getViewData()); + $this->assertNull($form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + public function testSubmitSingleExpandedRequiredNothingChecked() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => true, + 'choices' => $this->choices, + )); + + $form->submit(null); + + $this->assertNull($form->getData()); + $this->assertSame(array( + 0 => false, + 1 => false, + 2 => false, + 3 => false, + 4 => false, + ), $form->getViewData()); + + $this->assertFalse($form[0]->getData()); + $this->assertFalse($form[1]->getData()); + $this->assertFalse($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertNull($form[0]->getViewData()); + $this->assertNull($form[1]->getViewData()); + $this->assertNull($form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + public function testSubmitSingleExpandedNonRequiredNothingChecked() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => false, + 'choices' => $this->choices, + )); + + $form->submit(null); + + $this->assertNull($form->getData()); + $this->assertSame(array( + 0 => false, + 1 => false, + 2 => false, + 3 => false, + 4 => false, + 'placeholder' => true, + ), $form->getViewData()); + + $this->assertTrue($form['placeholder']->getData()); + $this->assertFalse($form[0]->getData()); + $this->assertFalse($form[1]->getData()); + $this->assertFalse($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertSame('', $form['placeholder']->getViewData()); + $this->assertNull($form[0]->getViewData()); + $this->assertNull($form[1]->getViewData()); + $this->assertNull($form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + public function testSubmitFalseToSingleExpandedRequiredDoesNotProduceExtraChildrenError() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => true, + 'choices' => $this->choices, + )); + + $form->submit(false); + + $this->assertEmpty($form->getExtraData()); + $this->assertNull($form->getData()); + } + + public function testSubmitFalseToSingleExpandedNonRequiredDoesNotProduceExtraChildrenError() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => false, + 'choices' => $this->choices, + )); + + $form->submit(false); + + $this->assertEmpty($form->getExtraData()); + $this->assertNull($form->getData()); + } + + public function testSubmitSingleExpandedWithEmptyChild() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => true, + 'choices' => array( + '' => 'Empty', + 1 => 'Not empty', + ), + )); + + $form->submit(''); + + $this->assertNull($form->getData()); + $this->assertTrue($form[0]->getData()); + $this->assertFalse($form[1]->getData()); + $this->assertSame('', $form[0]->getViewData()); + $this->assertNull($form[1]->getViewData()); + } + + public function testSubmitSingleExpandedObjectChoices() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => true, + 'choice_list' => new ObjectChoiceList( + $this->objectChoices, + // label path + 'name', + array(), + null, + // value path + 'id' + ), + )); + + $form->submit('2'); + + $this->assertSame($this->objectChoices[1], $form->getData()); + $this->assertFalse($form[0]->getData()); + $this->assertTrue($form[1]->getData()); + $this->assertFalse($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertNull($form[0]->getViewData()); + $this->assertSame('2', $form[1]->getViewData()); + $this->assertNull($form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + public function testSubmitSingleExpandedNumericChoices() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => true, + 'choices' => $this->numericChoices, + )); + + $form->submit('1'); + + $this->assertSame(1, $form->getData()); + $this->assertFalse($form[0]->getData()); + $this->assertTrue($form[1]->getData()); + $this->assertFalse($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertNull($form[0]->getViewData()); + $this->assertSame('1', $form[1]->getViewData()); + $this->assertNull($form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + public function testSubmitMultipleExpanded() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => true, + 'expanded' => true, + 'choices' => $this->choices, + )); + + $form->submit(array('a', 'c')); + + $this->assertSame(array('a', 'c'), $form->getData()); + $this->assertTrue($form[0]->getData()); + $this->assertFalse($form[1]->getData()); + $this->assertTrue($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertSame('a', $form[0]->getViewData()); + $this->assertNull($form[1]->getViewData()); + $this->assertSame('c', $form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + public function testSubmitMultipleExpandedEmpty() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => true, + 'expanded' => true, + 'choices' => $this->choices, + )); + + $form->submit(array()); + + $this->assertSame(array(), $form->getData()); + $this->assertFalse($form[0]->getData()); + $this->assertFalse($form[1]->getData()); + $this->assertFalse($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertNull($form[0]->getViewData()); + $this->assertNull($form[1]->getViewData()); + $this->assertNull($form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + public function testSubmitMultipleExpandedWithEmptyChild() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => true, + 'expanded' => true, + 'choices' => array( + '' => 'Empty', + 1 => 'Not Empty', + 2 => 'Not Empty 2', + ) + )); + + $form->submit(array('', '2')); + + $this->assertSame(array('', 2), $form->getData()); + $this->assertTrue($form[0]->getData()); + $this->assertFalse($form[1]->getData()); + $this->assertTrue($form[2]->getData()); + $this->assertSame('', $form[0]->getViewData()); + $this->assertNull($form[1]->getViewData()); + $this->assertSame('2', $form[2]->getViewData()); + } + + public function testSubmitMultipleExpandedObjectChoices() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => true, + 'expanded' => true, + 'choice_list' => new ObjectChoiceList( + $this->objectChoices, + // label path + 'name', + array(), + null, + // value path + 'id' + ), + )); + + $form->submit(array('1', '2')); + + $this->assertSame(array($this->objectChoices[0], $this->objectChoices[1]), $form->getData()); + $this->assertTrue($form[0]->getData()); + $this->assertTrue($form[1]->getData()); + $this->assertFalse($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertSame('1', $form[0]->getViewData()); + $this->assertSame('2', $form[1]->getViewData()); + $this->assertNull($form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + public function testSubmitMultipleExpandedNumericChoices() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => true, + 'expanded' => true, + 'choices' => $this->numericChoices, + )); + + $form->submit(array('1', '2')); + + $this->assertSame(array(1, 2), $form->getData()); + $this->assertFalse($form[0]->getData()); + $this->assertTrue($form[1]->getData()); + $this->assertTrue($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertNull($form[0]->getViewData()); + $this->assertSame('1', $form[1]->getViewData()); + $this->assertSame('2', $form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + /* + * We need this functionality to create choice fields for Boolean types, + * e.g. false => 'No', true => 'Yes' + */ + public function testSetDataSingleNonExpandedAcceptsBoolean() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => false, + 'choices' => $this->numericChoices, + )); + + $form->setData(false); + + $this->assertFalse($form->getData()); + $this->assertEquals('0', $form->getViewData()); + } + + public function testSetDataMultipleNonExpandedAcceptsBoolean() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => true, + 'expanded' => false, + 'choices' => $this->numericChoices, + )); + + $form->setData(array(false, true)); + + $this->assertEquals(array(false, true), $form->getData()); + $this->assertEquals(array('0', '1'), $form->getViewData()); + } + + public function testPassRequiredToView() + { + $form = $this->factory->create('choice', null, array( + 'choices' => $this->choices, + )); + $view = $form->createView(); + + $this->assertTrue($view->vars['required']); + } + + public function testPassNonRequiredToView() + { + $form = $this->factory->create('choice', null, array( + 'required' => false, + 'choices' => $this->choices, + )); + $view = $form->createView(); + + $this->assertFalse($view->vars['required']); + } + + public function testPassMultipleToView() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => true, + 'choices' => $this->choices, + )); + $view = $form->createView(); + + $this->assertTrue($view->vars['multiple']); + } + + public function testPassExpandedToView() + { + $form = $this->factory->create('choice', null, array( + 'expanded' => true, + 'choices' => $this->choices, + )); + $view = $form->createView(); + + $this->assertTrue($view->vars['expanded']); + } + + public function testEmptyValueIsNullByDefaultIfRequired() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'required' => true, + 'choices' => $this->choices, + )); + $view = $form->createView(); + + $this->assertNull($view->vars['empty_value']); + } + + public function testEmptyValueIsEmptyStringByDefaultIfNotRequired() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'required' => false, + 'choices' => $this->choices, + )); + $view = $form->createView(); + + $this->assertSame('', $view->vars['empty_value']); + } + + /** + * @dataProvider getOptionsWithEmptyValue + */ + public function testPassEmptyValueToView($multiple, $expanded, $required, $emptyValue, $viewValue) + { + $form = $this->factory->create('choice', null, array( + 'multiple' => $multiple, + 'expanded' => $expanded, + 'required' => $required, + 'empty_value' => $emptyValue, + 'choices' => $this->choices, + )); + $view = $form->createView(); + + $this->assertEquals($viewValue, $view->vars['empty_value']); + } + + /** + * @dataProvider getOptionsWithEmptyValue + */ + public function testDontPassEmptyValueIfContainedInChoices($multiple, $expanded, $required, $emptyValue, $viewValue) + { + $form = $this->factory->create('choice', null, array( + 'multiple' => $multiple, + 'expanded' => $expanded, + 'required' => $required, + 'empty_value' => $emptyValue, + 'choices' => array('a' => 'A', '' => 'Empty'), + )); + $view = $form->createView(); + + $this->assertNull($view->vars['empty_value']); + } + + public function getOptionsWithEmptyValue() + { + return array( + // single non-expanded + array(false, false, false, 'foobar', 'foobar'), + array(false, false, false, '', ''), + array(false, false, false, null, null), + array(false, false, false, false, null), + array(false, false, true, 'foobar', 'foobar'), + array(false, false, true, '', ''), + array(false, false, true, null, null), + array(false, false, true, false, null), + // single expanded + array(false, true, false, 'foobar', 'foobar'), + // radios should never have an empty label + array(false, true, false, '', 'None'), + array(false, true, false, null, null), + array(false, true, false, false, null), + array(false, true, true, 'foobar', 'foobar'), + // radios should never have an empty label + array(false, true, true, '', 'None'), + array(false, true, true, null, null), + array(false, true, true, false, null), + // multiple non-expanded + array(true, false, false, 'foobar', null), + array(true, false, false, '', null), + array(true, false, false, null, null), + array(true, false, false, false, null), + array(true, false, true, 'foobar', null), + array(true, false, true, '', null), + array(true, false, true, null, null), + array(true, false, true, false, null), + // multiple expanded + array(true, true, false, 'foobar', null), + array(true, true, false, '', null), + array(true, true, false, null, null), + array(true, true, false, false, null), + array(true, true, true, 'foobar', null), + array(true, true, true, '', null), + array(true, true, true, null, null), + array(true, true, true, false, null), + ); + } + + public function testPassChoicesToView() + { + $choices = array('a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D'); + $form = $this->factory->create('choice', null, array( + 'choices' => $choices, + )); + $view = $form->createView(); + + $this->assertEquals(array( + new ChoiceView('a', 'a', 'A'), + new ChoiceView('b', 'b', 'B'), + new ChoiceView('c', 'c', 'C'), + new ChoiceView('d', 'd', 'D'), + ), $view->vars['choices']); + } + + public function testPassPreferredChoicesToView() + { + $choices = array('a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D'); + $form = $this->factory->create('choice', null, array( + 'choices' => $choices, + 'preferred_choices' => array('b', 'd'), + )); + $view = $form->createView(); + + $this->assertEquals(array( + 0 => new ChoiceView('a', 'a', 'A'), + 2 => new ChoiceView('c', 'c', 'C'), + ), $view->vars['choices']); + $this->assertEquals(array( + 1 => new ChoiceView('b', 'b', 'B'), + 3 => new ChoiceView('d', 'd', 'D'), + ), $view->vars['preferred_choices']); + } + + public function testPassHierarchicalChoicesToView() + { + $form = $this->factory->create('choice', null, array( + 'choices' => $this->groupedChoices, + 'preferred_choices' => array('b', 'd'), + )); + $view = $form->createView(); + + $this->assertEquals(array( + 'Symfony' => array( + 0 => new ChoiceView('a', 'a', 'Bernhard'), + 2 => new ChoiceView('c', 'c', 'Kris'), + ), + 'Doctrine' => array( + 4 => new ChoiceView('e', 'e', 'Roman'), + ), + ), $view->vars['choices']); + $this->assertEquals(array( + 'Symfony' => array( + 1 => new ChoiceView('b', 'b', 'Fabien'), + ), + 'Doctrine' => array( + 3 => new ChoiceView('d', 'd', 'Jon'), + ), + ), $view->vars['preferred_choices']); + } + + public function testPassChoiceDataToView() + { + $obj1 = (object) array('value' => 'a', 'label' => 'A'); + $obj2 = (object) array('value' => 'b', 'label' => 'B'); + $obj3 = (object) array('value' => 'c', 'label' => 'C'); + $obj4 = (object) array('value' => 'd', 'label' => 'D'); + $form = $this->factory->create('choice', null, array( + 'choice_list' => new ObjectChoiceList(array($obj1, $obj2, $obj3, $obj4), 'label', array(), null, 'value'), + )); + $view = $form->createView(); + + $this->assertEquals(array( + new ChoiceView($obj1, 'a', 'A'), + new ChoiceView($obj2, 'b', 'B'), + new ChoiceView($obj3, 'c', 'C'), + new ChoiceView($obj4, 'd', 'D'), + ), $view->vars['choices']); + } + + public function testAdjustFullNameForMultipleNonExpanded() + { + $form = $this->factory->createNamed('name', 'choice', null, array( + 'multiple' => true, + 'expanded' => false, + 'choices' => $this->choices, + )); + $view = $form->createView(); + + $this->assertSame('name[]', $view->vars['full_name']); + } + + // https://github.com/symfony/symfony/issues/3298 + public function testInitializeWithEmptyChoices() + { + $this->factory->createNamed('name', 'choice', null, array( + 'choices' => array(), + )); + } + + public function testInitializeWithDefaultObjectChoice() + { + $obj1 = (object) array('value' => 'a', 'label' => 'A'); + $obj2 = (object) array('value' => 'b', 'label' => 'B'); + $obj3 = (object) array('value' => 'c', 'label' => 'C'); + $obj4 = (object) array('value' => 'd', 'label' => 'D'); + + $form = $this->factory->create('choice', null, array( + 'choice_list' => new ObjectChoiceList(array($obj1, $obj2, $obj3, $obj4), 'label', array(), null, 'value'), + // Used to break because "data_class" was inferred, which needs to + // remain null in every case (because it refers to the view format) + 'data' => $obj3, + )); + + // Trigger data initialization + $form->getViewData(); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php new file mode 100644 index 00000000..be3ad9db --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php @@ -0,0 +1,200 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Form; + +class CollectionTypeTest extends \Symfony\Component\Form\Test\TypeTestCase +{ + public function testContainsNoChildByDefault() + { + $form = $this->factory->create('collection', null, array( + 'type' => 'text', + )); + + $this->assertCount(0, $form); + } + + public function testSetDataAdjustsSize() + { + $form = $this->factory->create('collection', null, array( + 'type' => 'text', + 'options' => array( + 'max_length' => 20, + ), + )); + $form->setData(array('foo@foo.com', 'foo@bar.com')); + + $this->assertInstanceOf('Symfony\Component\Form\Form', $form[0]); + $this->assertInstanceOf('Symfony\Component\Form\Form', $form[1]); + $this->assertCount(2, $form); + $this->assertEquals('foo@foo.com', $form[0]->getData()); + $this->assertEquals('foo@bar.com', $form[1]->getData()); + $this->assertEquals(20, $form[0]->getConfig()->getOption('max_length')); + $this->assertEquals(20, $form[1]->getConfig()->getOption('max_length')); + + $form->setData(array('foo@baz.com')); + $this->assertInstanceOf('Symfony\Component\Form\Form', $form[0]); + $this->assertFalse(isset($form[1])); + $this->assertCount(1, $form); + $this->assertEquals('foo@baz.com', $form[0]->getData()); + $this->assertEquals(20, $form[0]->getConfig()->getOption('max_length')); + } + + public function testThrowsExceptionIfObjectIsNotTraversable() + { + $form = $this->factory->create('collection', null, array( + 'type' => 'text', + )); + $this->setExpectedException('Symfony\Component\Form\Exception\UnexpectedTypeException'); + $form->setData(new \stdClass()); + } + + public function testNotResizedIfSubmittedWithMissingData() + { + $form = $this->factory->create('collection', null, array( + 'type' => 'text', + )); + $form->setData(array('foo@foo.com', 'bar@bar.com')); + $form->submit(array('foo@bar.com')); + + $this->assertTrue($form->has('0')); + $this->assertTrue($form->has('1')); + $this->assertEquals('foo@bar.com', $form[0]->getData()); + $this->assertEquals('', $form[1]->getData()); + } + + public function testResizedDownIfSubmittedWithMissingDataAndAllowDelete() + { + $form = $this->factory->create('collection', null, array( + 'type' => 'text', + 'allow_delete' => true, + )); + $form->setData(array('foo@foo.com', 'bar@bar.com')); + $form->submit(array('foo@foo.com')); + + $this->assertTrue($form->has('0')); + $this->assertFalse($form->has('1')); + $this->assertEquals('foo@foo.com', $form[0]->getData()); + $this->assertEquals(array('foo@foo.com'), $form->getData()); + } + + public function testNotResizedIfSubmittedWithExtraData() + { + $form = $this->factory->create('collection', null, array( + 'type' => 'text', + )); + $form->setData(array('foo@bar.com')); + $form->submit(array('foo@foo.com', 'bar@bar.com')); + + $this->assertTrue($form->has('0')); + $this->assertFalse($form->has('1')); + $this->assertEquals('foo@foo.com', $form[0]->getData()); + } + + public function testResizedUpIfSubmittedWithExtraDataAndAllowAdd() + { + $form = $this->factory->create('collection', null, array( + 'type' => 'text', + 'allow_add' => true, + )); + $form->setData(array('foo@bar.com')); + $form->submit(array('foo@bar.com', 'bar@bar.com')); + + $this->assertTrue($form->has('0')); + $this->assertTrue($form->has('1')); + $this->assertEquals('foo@bar.com', $form[0]->getData()); + $this->assertEquals('bar@bar.com', $form[1]->getData()); + $this->assertEquals(array('foo@bar.com', 'bar@bar.com'), $form->getData()); + } + + public function testAllowAddButNoPrototype() + { + $form = $this->factory->create('collection', null, array( + 'type' => 'form', + 'allow_add' => true, + 'prototype' => false, + )); + + $this->assertFalse($form->has('__name__')); + } + + public function testPrototypeMultipartPropagation() + { + $form = $this->factory + ->create('collection', null, array( + 'type' => 'file', + 'allow_add' => true, + 'prototype' => true, + )) + ; + + $this->assertTrue($form->createView()->vars['multipart']); + } + + public function testGetDataDoesNotContainsPrototypeNameBeforeDataAreSet() + { + $form = $this->factory->create('collection', array(), array( + 'type' => 'file', + 'prototype' => true, + 'allow_add' => true, + )); + + $data = $form->getData(); + $this->assertFalse(isset($data['__name__'])); + } + + public function testGetDataDoesNotContainsPrototypeNameAfterDataAreSet() + { + $form = $this->factory->create('collection', array(), array( + 'type' => 'file', + 'allow_add' => true, + 'prototype' => true, + )); + + $form->setData(array('foobar.png')); + $data = $form->getData(); + $this->assertFalse(isset($data['__name__'])); + } + + public function testPrototypeNameOption() + { + $form = $this->factory->create('collection', null, array( + 'type' => 'form', + 'prototype' => true, + 'allow_add' => true, + )); + + $this->assertSame('__name__', $form->getConfig()->getAttribute('prototype')->getName(), '__name__ is the default'); + + $form = $this->factory->create('collection', null, array( + 'type' => 'form', + 'prototype' => true, + 'allow_add' => true, + 'prototype_name' => '__test__', + )); + + $this->assertSame('__test__', $form->getConfig()->getAttribute('prototype')->getName()); + } + + public function testPrototypeDefaultLabel() + { + $form = $this->factory->create('collection', array(), array( + 'type' => 'file', + 'allow_add' => true, + 'prototype' => true, + 'prototype_name' => '__test__', + )); + + $this->assertSame('__test__label__', $form->createView()->vars['prototype']->vars['label']); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/CountryTypeTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/CountryTypeTest.php new file mode 100644 index 00000000..1d56e2a0 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/CountryTypeTest.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Extension\Core\View\ChoiceView; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class CountryTypeTest extends TypeTestCase +{ + protected function setUp() + { + IntlTestHelper::requireIntl($this); + + parent::setUp(); + } + + public function testCountriesAreSelectable() + { + $form = $this->factory->create('country'); + $view = $form->createView(); + $choices = $view->vars['choices']; + + // Don't check objects for identity + $this->assertContains(new ChoiceView('DE', 'DE', 'Germany'), $choices, '', false, false); + $this->assertContains(new ChoiceView('GB', 'GB', 'United Kingdom'), $choices, '', false, false); + $this->assertContains(new ChoiceView('US', 'US', 'United States'), $choices, '', false, false); + $this->assertContains(new ChoiceView('FR', 'FR', 'France'), $choices, '', false, false); + $this->assertContains(new ChoiceView('MY', 'MY', 'Malaysia'), $choices, '', false, false); + } + + public function testUnknownCountryIsNotIncluded() + { + $form = $this->factory->create('country', 'country'); + $view = $form->createView(); + $choices = $view->vars['choices']; + + foreach ($choices as $choice) { + if ('ZZ' === $choice->value) { + $this->fail('Should not contain choice "ZZ"'); + } + } + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/CurrencyTypeTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/CurrencyTypeTest.php new file mode 100644 index 00000000..b0eb6dc0 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/CurrencyTypeTest.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Extension\Core\View\ChoiceView; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class CurrencyTypeTest extends TypeTestCase +{ + protected function setUp() + { + IntlTestHelper::requireIntl($this); + + parent::setUp(); + } + + public function testCurrenciesAreSelectable() + { + $form = $this->factory->create('currency'); + $view = $form->createView(); + $choices = $view->vars['choices']; + + $this->assertContains(new ChoiceView('EUR', 'EUR', 'Euro'), $choices, '', false, false); + $this->assertContains(new ChoiceView('USD', 'USD', 'US Dollar'), $choices, '', false, false); + $this->assertContains(new ChoiceView('SIT', 'SIT', 'Slovenian Tolar'), $choices, '', false, false); + } + +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php new file mode 100644 index 00000000..b9c1ebad --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php @@ -0,0 +1,477 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\FormError; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class DateTimeTypeTest extends TypeTestCase +{ + protected function setUp() + { + IntlTestHelper::requireIntl($this); + + parent::setUp(); + } + + public function testSubmitDateTime() + { + $form = $this->factory->create('datetime', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'date_widget' => 'choice', + 'time_widget' => 'choice', + 'input' => 'datetime', + )); + + $form->submit(array( + 'date' => array( + 'day' => '2', + 'month' => '6', + 'year' => '2010', + ), + 'time' => array( + 'hour' => '3', + 'minute' => '4', + ), + )); + + $dateTime = new \DateTime('2010-06-02 03:04:00 UTC'); + + $this->assertDateTimeEquals($dateTime, $form->getData()); + } + + public function testSubmitString() + { + $form = $this->factory->create('datetime', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'string', + 'date_widget' => 'choice', + 'time_widget' => 'choice', + )); + + $form->submit(array( + 'date' => array( + 'day' => '2', + 'month' => '6', + 'year' => '2010', + ), + 'time' => array( + 'hour' => '3', + 'minute' => '4', + ), + )); + + $this->assertEquals('2010-06-02 03:04:00', $form->getData()); + } + + public function testSubmitTimestamp() + { + $form = $this->factory->create('datetime', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'timestamp', + 'date_widget' => 'choice', + 'time_widget' => 'choice', + )); + + $form->submit(array( + 'date' => array( + 'day' => '2', + 'month' => '6', + 'year' => '2010', + ), + 'time' => array( + 'hour' => '3', + 'minute' => '4', + ), + )); + + $dateTime = new \DateTime('2010-06-02 03:04:00 UTC'); + + $this->assertEquals($dateTime->format('U'), $form->getData()); + } + + public function testSubmitWithoutMinutes() + { + $form = $this->factory->create('datetime', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'date_widget' => 'choice', + 'time_widget' => 'choice', + 'input' => 'datetime', + 'with_minutes' => false, + )); + + $form->setData(new \DateTime('2010-06-02 03:04:05 UTC')); + + $input = array( + 'date' => array( + 'day' => '2', + 'month' => '6', + 'year' => '2010', + ), + 'time' => array( + 'hour' => '3', + ), + ); + + $form->submit($input); + + $this->assertDateTimeEquals(new \DateTime('2010-06-02 03:00:00 UTC'), $form->getData()); + } + + public function testSubmitWithSeconds() + { + $form = $this->factory->create('datetime', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'date_widget' => 'choice', + 'time_widget' => 'choice', + 'input' => 'datetime', + 'with_seconds' => true, + )); + + $form->setData(new \DateTime('2010-06-02 03:04:05 UTC')); + + $input = array( + 'date' => array( + 'day' => '2', + 'month' => '6', + 'year' => '2010', + ), + 'time' => array( + 'hour' => '3', + 'minute' => '4', + 'second' => '5', + ), + ); + + $form->submit($input); + + $this->assertDateTimeEquals(new \DateTime('2010-06-02 03:04:05 UTC'), $form->getData()); + } + + public function testSubmitDifferentTimezones() + { + $form = $this->factory->create('datetime', null, array( + 'model_timezone' => 'America/New_York', + 'view_timezone' => 'Pacific/Tahiti', + 'date_widget' => 'choice', + 'time_widget' => 'choice', + 'input' => 'string', + 'with_seconds' => true, + )); + + $dateTime = new \DateTime('2010-06-02 03:04:05 Pacific/Tahiti'); + + $form->submit(array( + 'date' => array( + 'day' => (int) $dateTime->format('d'), + 'month' => (int) $dateTime->format('m'), + 'year' => (int) $dateTime->format('Y'), + ), + 'time' => array( + 'hour' => (int) $dateTime->format('H'), + 'minute' => (int) $dateTime->format('i'), + 'second' => (int) $dateTime->format('s'), + ), + )); + + $dateTime->setTimezone(new \DateTimeZone('America/New_York')); + + $this->assertEquals($dateTime->format('Y-m-d H:i:s'), $form->getData()); + } + + public function testSubmitDifferentTimezonesDateTime() + { + $form = $this->factory->create('datetime', null, array( + 'model_timezone' => 'America/New_York', + 'view_timezone' => 'Pacific/Tahiti', + 'widget' => 'single_text', + 'input' => 'datetime', + )); + + $outputTime = new \DateTime('2010-06-02 03:04:00 Pacific/Tahiti'); + + $form->submit('2010-06-02T03:04:00-10:00'); + + $outputTime->setTimezone(new \DateTimeZone('America/New_York')); + + $this->assertDateTimeEquals($outputTime, $form->getData()); + $this->assertEquals('2010-06-02T03:04:00-10:00', $form->getViewData()); + } + + public function testSubmitStringSingleText() + { + $form = $this->factory->create('datetime', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'string', + 'widget' => 'single_text', + )); + + $form->submit('2010-06-02T03:04:00Z'); + + $this->assertEquals('2010-06-02 03:04:00', $form->getData()); + $this->assertEquals('2010-06-02T03:04:00Z', $form->getViewData()); + } + + public function testSubmitStringSingleTextWithSeconds() + { + $form = $this->factory->create('datetime', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'string', + 'widget' => 'single_text', + 'with_seconds' => true, + )); + + $form->submit('2010-06-02T03:04:05Z'); + + $this->assertEquals('2010-06-02 03:04:05', $form->getData()); + $this->assertEquals('2010-06-02T03:04:05Z', $form->getViewData()); + } + + public function testSubmitDifferentPattern() + { + $form = $this->factory->create('datetime', null, array( + 'date_format' => 'MM*yyyy*dd', + 'date_widget' => 'single_text', + 'time_widget' => 'single_text', + 'input' => 'datetime', + )); + + $dateTime = new \DateTime('2010-06-02 03:04'); + + $form->submit(array( + 'date' => '06*2010*02', + 'time' => '03:04', + )); + + $this->assertDateTimeEquals($dateTime, $form->getData()); + } + + // Bug fix + public function testInitializeWithDateTime() + { + // Throws an exception if "data_class" option is not explicitly set + // to null in the type + $this->factory->create('datetime', new \DateTime()); + } + + public function testSingleTextWidgetShouldUseTheRightInputType() + { + $form = $this->factory->create('datetime', null, array( + 'widget' => 'single_text', + )); + + $view = $form->createView(); + $this->assertEquals('datetime', $view->vars['type']); + } + + public function testPassDefaultEmptyValueToViewIfNotRequired() + { + $form = $this->factory->create('datetime', null, array( + 'required' => false, + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('', $view['date']['year']->vars['empty_value']); + $this->assertSame('', $view['date']['month']->vars['empty_value']); + $this->assertSame('', $view['date']['day']->vars['empty_value']); + $this->assertSame('', $view['time']['hour']->vars['empty_value']); + $this->assertSame('', $view['time']['minute']->vars['empty_value']); + $this->assertSame('', $view['time']['second']->vars['empty_value']); + } + + public function testPassNoEmptyValueToViewIfRequired() + { + $form = $this->factory->create('datetime', null, array( + 'required' => true, + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertNull($view['date']['year']->vars['empty_value']); + $this->assertNull($view['date']['month']->vars['empty_value']); + $this->assertNull($view['date']['day']->vars['empty_value']); + $this->assertNull($view['time']['hour']->vars['empty_value']); + $this->assertNull($view['time']['minute']->vars['empty_value']); + $this->assertNull($view['time']['second']->vars['empty_value']); + } + + public function testPassEmptyValueAsString() + { + $form = $this->factory->create('datetime', null, array( + 'empty_value' => 'Empty', + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('Empty', $view['date']['year']->vars['empty_value']); + $this->assertSame('Empty', $view['date']['month']->vars['empty_value']); + $this->assertSame('Empty', $view['date']['day']->vars['empty_value']); + $this->assertSame('Empty', $view['time']['hour']->vars['empty_value']); + $this->assertSame('Empty', $view['time']['minute']->vars['empty_value']); + $this->assertSame('Empty', $view['time']['second']->vars['empty_value']); + } + + public function testPassEmptyValueAsArray() + { + $form = $this->factory->create('datetime', null, array( + 'empty_value' => array( + 'year' => 'Empty year', + 'month' => 'Empty month', + 'day' => 'Empty day', + 'hour' => 'Empty hour', + 'minute' => 'Empty minute', + 'second' => 'Empty second', + ), + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('Empty year', $view['date']['year']->vars['empty_value']); + $this->assertSame('Empty month', $view['date']['month']->vars['empty_value']); + $this->assertSame('Empty day', $view['date']['day']->vars['empty_value']); + $this->assertSame('Empty hour', $view['time']['hour']->vars['empty_value']); + $this->assertSame('Empty minute', $view['time']['minute']->vars['empty_value']); + $this->assertSame('Empty second', $view['time']['second']->vars['empty_value']); + } + + public function testPassEmptyValueAsPartialArrayAddEmptyIfNotRequired() + { + $form = $this->factory->create('datetime', null, array( + 'required' => false, + 'empty_value' => array( + 'year' => 'Empty year', + 'day' => 'Empty day', + 'hour' => 'Empty hour', + 'second' => 'Empty second', + ), + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('Empty year', $view['date']['year']->vars['empty_value']); + $this->assertSame('', $view['date']['month']->vars['empty_value']); + $this->assertSame('Empty day', $view['date']['day']->vars['empty_value']); + $this->assertSame('Empty hour', $view['time']['hour']->vars['empty_value']); + $this->assertSame('', $view['time']['minute']->vars['empty_value']); + $this->assertSame('Empty second', $view['time']['second']->vars['empty_value']); + } + + public function testPassEmptyValueAsPartialArrayAddNullIfRequired() + { + $form = $this->factory->create('datetime', null, array( + 'required' => true, + 'empty_value' => array( + 'year' => 'Empty year', + 'day' => 'Empty day', + 'hour' => 'Empty hour', + 'second' => 'Empty second', + ), + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('Empty year', $view['date']['year']->vars['empty_value']); + $this->assertNull($view['date']['month']->vars['empty_value']); + $this->assertSame('Empty day', $view['date']['day']->vars['empty_value']); + $this->assertSame('Empty hour', $view['time']['hour']->vars['empty_value']); + $this->assertNull($view['time']['minute']->vars['empty_value']); + $this->assertSame('Empty second', $view['time']['second']->vars['empty_value']); + } + + public function testPassHtml5TypeIfSingleTextAndHtml5Format() + { + $form = $this->factory->create('datetime', null, array( + 'widget' => 'single_text', + )); + + $view = $form->createView(); + $this->assertSame('datetime', $view->vars['type']); + } + + public function testDontPassHtml5TypeIfNotHtml5Format() + { + $form = $this->factory->create('datetime', null, array( + 'widget' => 'single_text', + 'format' => 'yyyy-MM-dd HH:mm', + )); + + $view = $form->createView(); + $this->assertFalse(isset($view->vars['type'])); + } + + public function testDontPassHtml5TypeIfNotSingleText() + { + $form = $this->factory->create('datetime', null, array( + 'widget' => 'text', + )); + + $view = $form->createView(); + $this->assertFalse(isset($view->vars['type'])); + } + + public function testDateTypeChoiceErrorsBubbleUp() + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('datetime', null); + + $form['date']->addError($error); + + $this->assertSame(array(), $form['date']->getErrors()); + $this->assertSame(array($error), $form->getErrors()); + } + + public function testDateTypeSingleTextErrorsBubbleUp() + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('datetime', null, array( + 'date_widget' => 'single_text' + )); + + $form['date']->addError($error); + + $this->assertSame(array(), $form['date']->getErrors()); + $this->assertSame(array($error), $form->getErrors()); + } + + public function testTimeTypeChoiceErrorsBubbleUp() + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('datetime', null); + + $form['time']->addError($error); + + $this->assertSame(array(), $form['time']->getErrors()); + $this->assertSame(array($error), $form->getErrors()); + } + + public function testTimeTypeSingleTextErrorsBubbleUp() + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('datetime', null, array( + 'time_widget' => 'single_text' + )); + + $form['time']->addError($error); + + $this->assertSame(array(), $form['time']->getErrors()); + $this->assertSame(array($error), $form->getErrors()); + } + +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php new file mode 100644 index 00000000..2aa155e7 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php @@ -0,0 +1,781 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Extension\Core\View\ChoiceView; +use Symfony\Component\Form\FormError; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class DateTypeTest extends TypeTestCase +{ + protected function setUp() + { + parent::setUp(); + + // we test against "de_AT", so we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testInvalidWidgetOption() + { + $this->factory->create('date', null, array( + 'widget' => 'fake_widget', + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testInvalidInputOption() + { + $this->factory->create('date', null, array( + 'input' => 'fake_input', + )); + } + + public function testSubmitFromSingleTextDateTimeWithDefaultFormat() + { + $form = $this->factory->create('date', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'single_text', + 'input' => 'datetime', + )); + + $form->submit('2010-06-02'); + + $this->assertDateTimeEquals(new \DateTime('2010-06-02 UTC'), $form->getData()); + $this->assertEquals('2010-06-02', $form->getViewData()); + } + + public function testSubmitFromSingleTextDateTime() + { + $form = $this->factory->create('date', null, array( + 'format' => \IntlDateFormatter::MEDIUM, + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'single_text', + 'input' => 'datetime', + )); + + $form->submit('2.6.2010'); + + $this->assertDateTimeEquals(new \DateTime('2010-06-02 UTC'), $form->getData()); + $this->assertEquals('02.06.2010', $form->getViewData()); + } + + public function testSubmitFromSingleTextString() + { + $form = $this->factory->create('date', null, array( + 'format' => \IntlDateFormatter::MEDIUM, + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'single_text', + 'input' => 'string', + )); + + $form->submit('2.6.2010'); + + $this->assertEquals('2010-06-02', $form->getData()); + $this->assertEquals('02.06.2010', $form->getViewData()); + } + + public function testSubmitFromSingleTextTimestamp() + { + $form = $this->factory->create('date', null, array( + 'format' => \IntlDateFormatter::MEDIUM, + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'single_text', + 'input' => 'timestamp', + )); + + $form->submit('2.6.2010'); + + $dateTime = new \DateTime('2010-06-02 UTC'); + + $this->assertEquals($dateTime->format('U'), $form->getData()); + $this->assertEquals('02.06.2010', $form->getViewData()); + } + + public function testSubmitFromSingleTextRaw() + { + $form = $this->factory->create('date', null, array( + 'format' => \IntlDateFormatter::MEDIUM, + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'single_text', + 'input' => 'array', + )); + + $form->submit('2.6.2010'); + + $output = array( + 'day' => '2', + 'month' => '6', + 'year' => '2010', + ); + + $this->assertEquals($output, $form->getData()); + $this->assertEquals('02.06.2010', $form->getViewData()); + } + + public function testSubmitFromText() + { + $form = $this->factory->create('date', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'text', + )); + + $text = array( + 'day' => '2', + 'month' => '6', + 'year' => '2010', + ); + + $form->submit($text); + + $dateTime = new \DateTime('2010-06-02 UTC'); + + $this->assertDateTimeEquals($dateTime, $form->getData()); + $this->assertEquals($text, $form->getViewData()); + } + + public function testSubmitFromChoice() + { + $form = $this->factory->create('date', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'choice', + )); + + $text = array( + 'day' => '2', + 'month' => '6', + 'year' => '2010', + ); + + $form->submit($text); + + $dateTime = new \DateTime('2010-06-02 UTC'); + + $this->assertDateTimeEquals($dateTime, $form->getData()); + $this->assertEquals($text, $form->getViewData()); + } + + public function testSubmitFromChoiceEmpty() + { + $form = $this->factory->create('date', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'choice', + 'required' => false, + )); + + $text = array( + 'day' => '', + 'month' => '', + 'year' => '', + ); + + $form->submit($text); + + $this->assertNull($form->getData()); + $this->assertEquals($text, $form->getViewData()); + } + + public function testSubmitFromInputDateTimeDifferentPattern() + { + $form = $this->factory->create('date', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'format' => 'MM*yyyy*dd', + 'widget' => 'single_text', + 'input' => 'datetime', + )); + + $form->submit('06*2010*02'); + + $this->assertDateTimeEquals(new \DateTime('2010-06-02 UTC'), $form->getData()); + $this->assertEquals('06*2010*02', $form->getViewData()); + } + + public function testSubmitFromInputStringDifferentPattern() + { + $form = $this->factory->create('date', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'format' => 'MM*yyyy*dd', + 'widget' => 'single_text', + 'input' => 'string', + )); + + $form->submit('06*2010*02'); + + $this->assertEquals('2010-06-02', $form->getData()); + $this->assertEquals('06*2010*02', $form->getViewData()); + } + + public function testSubmitFromInputTimestampDifferentPattern() + { + $form = $this->factory->create('date', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'format' => 'MM*yyyy*dd', + 'widget' => 'single_text', + 'input' => 'timestamp', + )); + + $form->submit('06*2010*02'); + + $dateTime = new \DateTime('2010-06-02 UTC'); + + $this->assertEquals($dateTime->format('U'), $form->getData()); + $this->assertEquals('06*2010*02', $form->getViewData()); + } + + public function testSubmitFromInputRawDifferentPattern() + { + $form = $this->factory->create('date', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'format' => 'MM*yyyy*dd', + 'widget' => 'single_text', + 'input' => 'array', + )); + + $form->submit('06*2010*02'); + + $output = array( + 'day' => '2', + 'month' => '6', + 'year' => '2010', + ); + + $this->assertEquals($output, $form->getData()); + $this->assertEquals('06*2010*02', $form->getViewData()); + } + + /** + * @dataProvider provideDateFormats + */ + public function testDatePatternWithFormatOption($format, $pattern) + { + $form = $this->factory->create('date', null, array( + 'format' => $format, + )); + + $view = $form->createView(); + + $this->assertEquals($pattern, $view->vars['date_pattern']); + } + + public function provideDateFormats() + { + return array( + array('dMy', '{{ day }}{{ month }}{{ year }}'), + array('d-M-yyyy', '{{ day }}-{{ month }}-{{ year }}'), + array('M d y', '{{ month }} {{ day }} {{ year }}'), + ); + } + + /** + * This test is to check that the strings '0', '1', '2', '3' are no accepted + * as valid IntlDateFormatter constants for FULL, LONG, MEDIUM or SHORT respectively. + * + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testThrowExceptionIfFormatIsNoPattern() + { + $this->factory->create('date', null, array( + 'format' => '0', + 'widget' => 'single_text', + 'input' => 'string', + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testThrowExceptionIfFormatDoesNotContainYearMonthAndDay() + { + $this->factory->create('date', null, array( + 'months' => array(6, 7), + 'format' => 'yy', + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testThrowExceptionIfFormatIsNoConstant() + { + $this->factory->create('date', null, array( + 'format' => 105, + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testThrowExceptionIfFormatIsInvalid() + { + $this->factory->create('date', null, array( + 'format' => array(), + )); + } + + public function testSetDataWithDifferentTimezones() + { + $form = $this->factory->create('date', null, array( + 'format' => \IntlDateFormatter::MEDIUM, + 'model_timezone' => 'America/New_York', + 'view_timezone' => 'Pacific/Tahiti', + 'input' => 'string', + 'widget' => 'single_text', + )); + + $form->setData('2010-06-02'); + + $this->assertEquals('01.06.2010', $form->getViewData()); + } + + public function testSetDataWithDifferentTimezonesDateTime() + { + $form = $this->factory->create('date', null, array( + 'format' => \IntlDateFormatter::MEDIUM, + 'model_timezone' => 'America/New_York', + 'view_timezone' => 'Pacific/Tahiti', + 'input' => 'datetime', + 'widget' => 'single_text', + )); + + $dateTime = new \DateTime('2010-06-02 America/New_York'); + + $form->setData($dateTime); + + $this->assertDateTimeEquals($dateTime, $form->getData()); + $this->assertEquals('01.06.2010', $form->getViewData()); + } + + public function testYearsOption() + { + $form = $this->factory->create('date', null, array( + 'years' => array(2010, 2011), + )); + + $view = $form->createView(); + + $this->assertEquals(array( + new ChoiceView('2010', '2010', '2010'), + new ChoiceView('2011', '2011', '2011'), + ), $view['year']->vars['choices']); + } + + public function testMonthsOption() + { + $form = $this->factory->create('date', null, array( + 'months' => array(6, 7), + )); + + $view = $form->createView(); + + $this->assertEquals(array( + new ChoiceView('6', '6', '06'), + new ChoiceView('7', '7', '07'), + ), $view['month']->vars['choices']); + } + + public function testMonthsOptionShortFormat() + { + $form = $this->factory->create('date', null, array( + 'months' => array(1, 4), + 'format' => 'dd.MMM.yy', + )); + + $view = $form->createView(); + + $this->assertEquals(array( + new ChoiceView('1', '1', 'Jän'), + new ChoiceView('4', '4', 'Apr.') + ), $view['month']->vars['choices']); + } + + public function testMonthsOptionLongFormat() + { + $form = $this->factory->create('date', null, array( + 'months' => array(1, 4), + 'format' => 'dd.MMMM.yy', + )); + + $view = $form->createView(); + + $this->assertEquals(array( + new ChoiceView('1', '1', 'Jänner'), + new ChoiceView('4', '4', 'April'), + ), $view['month']->vars['choices']); + } + + public function testMonthsOptionLongFormatWithDifferentTimezone() + { + $form = $this->factory->create('date', null, array( + 'months' => array(1, 4), + 'format' => 'dd.MMMM.yy', + )); + + $view = $form->createView(); + + $this->assertEquals(array( + new ChoiceView('1', '1', 'Jänner'), + new ChoiceView('4', '4', 'April'), + ), $view['month']->vars['choices']); + } + + public function testIsDayWithinRangeReturnsTrueIfWithin() + { + $form = $this->factory->create('date', null, array( + 'days' => array(6, 7), + )); + + $view = $form->createView(); + + $this->assertEquals(array( + new ChoiceView('6', '6', '06'), + new ChoiceView('7', '7', '07'), + ), $view['day']->vars['choices']); + } + + public function testIsPartiallyFilledReturnsFalseIfSingleText() + { + $this->markTestIncomplete('Needs to be reimplemented using validators'); + + $form = $this->factory->create('date', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'single_text', + )); + + $form->submit('7.6.2010'); + + $this->assertFalse($form->isPartiallyFilled()); + } + + public function testIsPartiallyFilledReturnsFalseIfChoiceAndCompletelyEmpty() + { + $this->markTestIncomplete('Needs to be reimplemented using validators'); + + $form = $this->factory->create('date', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'choice', + )); + + $form->submit(array( + 'day' => '', + 'month' => '', + 'year' => '', + )); + + $this->assertFalse($form->isPartiallyFilled()); + } + + public function testIsPartiallyFilledReturnsFalseIfChoiceAndCompletelyFilled() + { + $this->markTestIncomplete('Needs to be reimplemented using validators'); + + $form = $this->factory->create('date', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'choice', + )); + + $form->submit(array( + 'day' => '2', + 'month' => '6', + 'year' => '2010', + )); + + $this->assertFalse($form->isPartiallyFilled()); + } + + public function testIsPartiallyFilledReturnsTrueIfChoiceAndDayEmpty() + { + $this->markTestIncomplete('Needs to be reimplemented using validators'); + + $form = $this->factory->create('date', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'choice', + )); + + $form->submit(array( + 'day' => '', + 'month' => '6', + 'year' => '2010', + )); + + $this->assertTrue($form->isPartiallyFilled()); + } + + public function testPassDatePatternToView() + { + $form = $this->factory->create('date'); + $view = $form->createView(); + + $this->assertSame('{{ day }}{{ month }}{{ year }}', $view->vars['date_pattern']); + } + + public function testPassDatePatternToViewDifferentFormat() + { + $form = $this->factory->create('date', null, array( + 'format' => \IntlDateFormatter::LONG, + )); + + $view = $form->createView(); + + $this->assertSame('{{ day }}{{ month }}{{ year }}', $view->vars['date_pattern']); + } + + public function testPassDatePatternToViewDifferentPattern() + { + $form = $this->factory->create('date', null, array( + 'format' => 'MMyyyydd' + )); + + $view = $form->createView(); + + $this->assertSame('{{ month }}{{ year }}{{ day }}', $view->vars['date_pattern']); + } + + public function testPassDatePatternToViewDifferentPatternWithSeparators() + { + $form = $this->factory->create('date', null, array( + 'format' => 'MM*yyyy*dd' + )); + + $view = $form->createView(); + + $this->assertSame('{{ month }}*{{ year }}*{{ day }}', $view->vars['date_pattern']); + } + + public function testDontPassDatePatternIfText() + { + $form = $this->factory->create('date', null, array( + 'widget' => 'single_text', + )); + $view = $form->createView(); + + $this->assertFalse(isset($view->vars['date_pattern'])); + } + + public function testPassWidgetToView() + { + $form = $this->factory->create('date', null, array( + 'widget' => 'single_text', + )); + $view = $form->createView(); + + $this->assertSame('single_text', $view->vars['widget']); + } + + // Bug fix + public function testInitializeWithDateTime() + { + // Throws an exception if "data_class" option is not explicitly set + // to null in the type + $this->factory->create('date', new \DateTime()); + } + + public function testSingleTextWidgetShouldUseTheRightInputType() + { + $form = $this->factory->create('date', null, array( + 'widget' => 'single_text', + )); + + $view = $form->createView(); + $this->assertEquals('date', $view->vars['type']); + } + + public function testPassDefaultEmptyValueToViewIfNotRequired() + { + $form = $this->factory->create('date', null, array( + 'required' => false, + )); + + $view = $form->createView(); + $this->assertSame('', $view['year']->vars['empty_value']); + $this->assertSame('', $view['month']->vars['empty_value']); + $this->assertSame('', $view['day']->vars['empty_value']); + } + + public function testPassNoEmptyValueToViewIfRequired() + { + $form = $this->factory->create('date', null, array( + 'required' => true, + )); + + $view = $form->createView(); + $this->assertNull($view['year']->vars['empty_value']); + $this->assertNull($view['month']->vars['empty_value']); + $this->assertNull($view['day']->vars['empty_value']); + } + + public function testPassEmptyValueAsString() + { + $form = $this->factory->create('date', null, array( + 'empty_value' => 'Empty', + )); + + $view = $form->createView(); + $this->assertSame('Empty', $view['year']->vars['empty_value']); + $this->assertSame('Empty', $view['month']->vars['empty_value']); + $this->assertSame('Empty', $view['day']->vars['empty_value']); + } + + public function testPassEmptyValueAsArray() + { + $form = $this->factory->create('date', null, array( + 'empty_value' => array( + 'year' => 'Empty year', + 'month' => 'Empty month', + 'day' => 'Empty day', + ), + )); + + $view = $form->createView(); + $this->assertSame('Empty year', $view['year']->vars['empty_value']); + $this->assertSame('Empty month', $view['month']->vars['empty_value']); + $this->assertSame('Empty day', $view['day']->vars['empty_value']); + } + + public function testPassEmptyValueAsPartialArrayAddEmptyIfNotRequired() + { + $form = $this->factory->create('date', null, array( + 'required' => false, + 'empty_value' => array( + 'year' => 'Empty year', + 'day' => 'Empty day', + ), + )); + + $view = $form->createView(); + $this->assertSame('Empty year', $view['year']->vars['empty_value']); + $this->assertSame('', $view['month']->vars['empty_value']); + $this->assertSame('Empty day', $view['day']->vars['empty_value']); + } + + public function testPassEmptyValueAsPartialArrayAddNullIfRequired() + { + $form = $this->factory->create('date', null, array( + 'required' => true, + 'empty_value' => array( + 'year' => 'Empty year', + 'day' => 'Empty day', + ), + )); + + $view = $form->createView(); + $this->assertSame('Empty year', $view['year']->vars['empty_value']); + $this->assertNull($view['month']->vars['empty_value']); + $this->assertSame('Empty day', $view['day']->vars['empty_value']); + } + + public function testPassHtml5TypeIfSingleTextAndHtml5Format() + { + $form = $this->factory->create('date', null, array( + 'widget' => 'single_text', + )); + + $view = $form->createView(); + $this->assertSame('date', $view->vars['type']); + } + + public function testDontPassHtml5TypeIfNotHtml5Format() + { + $form = $this->factory->create('date', null, array( + 'widget' => 'single_text', + 'format' => \IntlDateFormatter::MEDIUM, + )); + + $view = $form->createView(); + $this->assertFalse(isset($view->vars['type'])); + } + + public function testDontPassHtml5TypeIfNotSingleText() + { + $form = $this->factory->create('date', null, array( + 'widget' => 'text', + )); + + $view = $form->createView(); + $this->assertFalse(isset($view->vars['type'])); + } + + public function provideCompoundWidgets() + { + return array( + array('text'), + array('choice'), + ); + } + + /** + * @dataProvider provideCompoundWidgets + */ + public function testYearErrorsBubbleUp($widget) + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('date', null, array( + 'widget' => $widget, + )); + $form['year']->addError($error); + + $this->assertSame(array(), $form['year']->getErrors()); + $this->assertSame(array($error), $form->getErrors()); + } + + /** + * @dataProvider provideCompoundWidgets + */ + public function testMonthErrorsBubbleUp($widget) + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('date', null, array( + 'widget' => $widget, + )); + $form['month']->addError($error); + + $this->assertSame(array(), $form['month']->getErrors()); + $this->assertSame(array($error), $form->getErrors()); + } + + /** + * @dataProvider provideCompoundWidgets + */ + public function testDayErrorsBubbleUp($widget) + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('date', null, array( + 'widget' => $widget, + )); + $form['day']->addError($error); + + $this->assertSame(array(), $form['day']->getErrors()); + $this->assertSame(array($error), $form->getErrors()); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php new file mode 100644 index 00000000..63556eb5 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +class FileTypeTest extends \Symfony\Component\Form\Test\TypeTestCase +{ + // https://github.com/symfony/symfony/pull/5028 + public function testSetData() + { + $form = $this->factory->createBuilder('file')->getForm(); + $data = $this->createUploadedFileMock('abcdef', 'original.jpg', true); + + $form->setData($data); + + $this->assertSame($data, $form->getData()); + } + + public function testSubmit() + { + $form = $this->factory->createBuilder('file')->getForm(); + $data = $this->createUploadedFileMock('abcdef', 'original.jpg', true); + + $form->submit($data); + + $this->assertSame($data, $form->getData()); + } + + // https://github.com/symfony/symfony/issues/6134 + public function testSubmitEmpty() + { + $form = $this->factory->createBuilder('file')->getForm(); + + $form->submit(null); + + $this->assertNull($form->getData()); + } + + public function testDontPassValueToView() + { + $form = $this->factory->create('file'); + $form->submit(array( + 'file' => $this->createUploadedFileMock('abcdef', 'original.jpg', true), + )); + $view = $form->createView(); + + $this->assertEquals('', $view->vars['value']); + } + + private function createUploadedFileMock($name, $originalName, $valid) + { + $file = $this + ->getMockBuilder('Symfony\Component\HttpFoundation\File\UploadedFile') + ->disableOriginalConstructor() + ->getMock() + ; + $file + ->expects($this->any()) + ->method('getBasename') + ->will($this->returnValue($name)) + ; + $file + ->expects($this->any()) + ->method('getClientOriginalName') + ->will($this->returnValue($originalName)) + ; + $file + ->expects($this->any()) + ->method('isValid') + ->will($this->returnValue($valid)) + ; + + return $file; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php new file mode 100644 index 00000000..cced82f4 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php @@ -0,0 +1,578 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\PropertyAccess\PropertyPath; +use Symfony\Component\Form\Form; +use Symfony\Component\Form\CallbackTransformer; +use Symfony\Component\Form\Tests\Fixtures\Author; +use Symfony\Component\Form\Tests\Fixtures\FixedDataTransformer; +use Symfony\Component\Form\FormError; + +class FormTest_AuthorWithoutRefSetter +{ + protected $reference; + + protected $referenceCopy; + + public function __construct($reference) + { + $this->reference = $reference; + $this->referenceCopy = $reference; + } + + // The returned object should be modified by reference without having + // to provide a setReference() method + public function getReference() + { + return $this->reference; + } + + // The returned object is a copy, so setReferenceCopy() must be used + // to update it + public function getReferenceCopy() + { + return is_object($this->referenceCopy) ? clone $this->referenceCopy : $this->referenceCopy; + } + + public function setReferenceCopy($reference) + { + $this->referenceCopy = $reference; + } +} + +class FormTypeTest extends BaseTypeTest +{ + public function testCreateFormInstances() + { + $this->assertInstanceOf('Symfony\Component\Form\Form', $this->factory->create('form')); + } + + public function testPassRequiredAsOption() + { + $form = $this->factory->create('form', null, array('required' => false)); + + $this->assertFalse($form->isRequired()); + + $form = $this->factory->create('form', null, array('required' => true)); + + $this->assertTrue($form->isRequired()); + } + + public function testSubmittedDataIsTrimmedBeforeTransforming() + { + $form = $this->factory->createBuilder('form') + ->addViewTransformer(new FixedDataTransformer(array( + null => '', + 'reverse[a]' => 'a', + ))) + ->setCompound(false) + ->getForm(); + + $form->submit(' a '); + + $this->assertEquals('a', $form->getViewData()); + $this->assertEquals('reverse[a]', $form->getData()); + } + + public function testSubmittedDataIsNotTrimmedBeforeTransformingIfNoTrimming() + { + $form = $this->factory->createBuilder('form', null, array('trim' => false)) + ->addViewTransformer(new FixedDataTransformer(array( + null => '', + 'reverse[ a ]' => ' a ', + ))) + ->setCompound(false) + ->getForm(); + + $form->submit(' a '); + + $this->assertEquals(' a ', $form->getViewData()); + $this->assertEquals('reverse[ a ]', $form->getData()); + } + + public function testNonReadOnlyFormWithReadOnlyParentIsReadOnly() + { + $view = $this->factory->createNamedBuilder('parent', 'form', null, array('read_only' => true)) + ->add('child', 'form') + ->getForm() + ->createView(); + + $this->assertTrue($view['child']->vars['read_only']); + } + + public function testReadOnlyFormWithNonReadOnlyParentIsReadOnly() + { + $view = $this->factory->createNamedBuilder('parent', 'form') + ->add('child', 'form', array('read_only' => true)) + ->getForm() + ->createView(); + + $this->assertTrue($view['child']->vars['read_only']); + } + + public function testNonReadOnlyFormWithNonReadOnlyParentIsNotReadOnly() + { + $view = $this->factory->createNamedBuilder('parent', 'form') + ->add('child', 'form') + ->getForm() + ->createView(); + + $this->assertFalse($view['child']->vars['read_only']); + } + + public function testPassMaxLengthToView() + { + $form = $this->factory->create('form', null, array('max_length' => 10)); + $view = $form->createView(); + + $this->assertSame(10, $view->vars['max_length']); + } + + public function testSubmitWithEmptyDataCreatesObjectIfClassAvailable() + { + $builder = $this->factory->createBuilder('form', null, array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + 'required' => false, + )); + $builder->add('firstName', 'text'); + $builder->add('lastName', 'text'); + $form = $builder->getForm(); + + $form->setData(null); + // partially empty, still an object is created + $form->submit(array('firstName' => 'Bernhard', 'lastName' => '')); + + $author = new Author(); + $author->firstName = 'Bernhard'; + $author->setLastName(''); + + $this->assertEquals($author, $form->getData()); + } + + public function testSubmitWithEmptyDataCreatesObjectIfInitiallySubmittedWithObject() + { + $builder = $this->factory->createBuilder('form', null, array( + // data class is inferred from the passed object + 'data' => new Author(), + 'required' => false, + )); + $builder->add('firstName', 'text'); + $builder->add('lastName', 'text'); + $form = $builder->getForm(); + + $form->setData(null); + // partially empty, still an object is created + $form->submit(array('firstName' => 'Bernhard', 'lastName' => '')); + + $author = new Author(); + $author->firstName = 'Bernhard'; + $author->setLastName(''); + + $this->assertEquals($author, $form->getData()); + } + + public function testSubmitWithEmptyDataCreatesArrayIfDataClassIsNull() + { + $builder = $this->factory->createBuilder('form', null, array( + 'data_class' => null, + 'required' => false, + )); + $builder->add('firstName', 'text'); + $form = $builder->getForm(); + + $form->setData(null); + $form->submit(array('firstName' => 'Bernhard')); + + $this->assertSame(array('firstName' => 'Bernhard'), $form->getData()); + } + + public function testSubmitEmptyWithEmptyDataCreatesNoObjectIfNotRequired() + { + $builder = $this->factory->createBuilder('form', null, array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + 'required' => false, + )); + $builder->add('firstName', 'text'); + $builder->add('lastName', 'text'); + $form = $builder->getForm(); + + $form->setData(null); + $form->submit(array('firstName' => '', 'lastName' => '')); + + $this->assertNull($form->getData()); + } + + public function testSubmitEmptyWithEmptyDataCreatesObjectIfRequired() + { + $builder = $this->factory->createBuilder('form', null, array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + 'required' => true, + )); + $builder->add('firstName', 'text'); + $builder->add('lastName', 'text'); + $form = $builder->getForm(); + + $form->setData(null); + $form->submit(array('firstName' => '', 'lastName' => '')); + + $this->assertEquals(new Author(), $form->getData()); + } + + /* + * We need something to write the field values into + */ + public function testSubmitWithEmptyDataStoresArrayIfNoClassAvailable() + { + $form = $this->factory->createBuilder('form') + ->add('firstName', 'text') + ->getForm(); + + $form->setData(null); + $form->submit(array('firstName' => 'Bernhard')); + + $this->assertSame(array('firstName' => 'Bernhard'), $form->getData()); + } + + public function testSubmitWithEmptyDataPassesEmptyStringToTransformerIfNotCompound() + { + $form = $this->factory->createBuilder('form') + ->addViewTransformer(new FixedDataTransformer(array( + // required for the initial, internal setData(null) + null => 'null', + // required to test that submit(null) is converted to '' + 'empty' => '', + ))) + ->setCompound(false) + ->getForm(); + + $form->submit(null); + + $this->assertSame('empty', $form->getData()); + } + + public function testSubmitWithEmptyDataUsesEmptyDataOption() + { + $author = new Author(); + + $builder = $this->factory->createBuilder('form', null, array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + 'empty_data' => $author, + )); + $builder->add('firstName', 'text'); + $form = $builder->getForm(); + + $form->submit(array('firstName' => 'Bernhard')); + + $this->assertSame($author, $form->getData()); + $this->assertEquals('Bernhard', $author->firstName); + } + + public function provideZeros() + { + return array( + array(0, '0'), + array('0', '0'), + array('00000', '00000'), + ); + } + + /** + * @dataProvider provideZeros + * @see https://github.com/symfony/symfony/issues/1986 + */ + public function testSetDataThroughParamsWithZero($data, $dataAsString) + { + $form = $this->factory->create('form', null, array( + 'data' => $data, + 'compound' => false, + )); + $view = $form->createView(); + + $this->assertFalse($form->isEmpty()); + + $this->assertSame($dataAsString, $view->vars['value']); + $this->assertSame($dataAsString, $form->getData()); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testAttributesException() + { + $this->factory->create('form', null, array('attr' => '')); + } + + public function testNameCanBeEmptyString() + { + $form = $this->factory->createNamed('', 'form'); + + $this->assertEquals('', $form->getName()); + } + + public function testSubformDoesntCallSetters() + { + $author = new FormTest_AuthorWithoutRefSetter(new Author()); + + $builder = $this->factory->createBuilder('form', $author); + $builder->add('reference', 'form', array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + )); + $builder->get('reference')->add('firstName', 'text'); + $form = $builder->getForm(); + + $form->submit(array( + // reference has a getter, but not setter + 'reference' => array( + 'firstName' => 'Foo', + ) + )); + + $this->assertEquals('Foo', $author->getReference()->firstName); + } + + public function testSubformCallsSettersIfTheObjectChanged() + { + // no reference + $author = new FormTest_AuthorWithoutRefSetter(null); + $newReference = new Author(); + + $builder = $this->factory->createBuilder('form', $author); + $builder->add('referenceCopy', 'form', array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + )); + $builder->get('referenceCopy')->add('firstName', 'text'); + $form = $builder->getForm(); + + $form['referenceCopy']->setData($newReference); // new author object + + $form->submit(array( + // referenceCopy has a getter that returns a copy + 'referenceCopy' => array( + 'firstName' => 'Foo', + ) + )); + + $this->assertEquals('Foo', $author->getReferenceCopy()->firstName); + } + + public function testSubformCallsSettersIfByReferenceIsFalse() + { + $author = new FormTest_AuthorWithoutRefSetter(new Author()); + + $builder = $this->factory->createBuilder('form', $author); + $builder->add('referenceCopy', 'form', array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + 'by_reference' => false + )); + $builder->get('referenceCopy')->add('firstName', 'text'); + $form = $builder->getForm(); + + $form->submit(array( + // referenceCopy has a getter that returns a copy + 'referenceCopy' => array( + 'firstName' => 'Foo', + ) + )); + + // firstName can only be updated if setReferenceCopy() was called + $this->assertEquals('Foo', $author->getReferenceCopy()->firstName); + } + + public function testSubformCallsSettersIfReferenceIsScalar() + { + $author = new FormTest_AuthorWithoutRefSetter('scalar'); + + $builder = $this->factory->createBuilder('form', $author); + $builder->add('referenceCopy', 'form'); + $builder->get('referenceCopy')->addViewTransformer(new CallbackTransformer( + function () {}, + function ($value) { // reverseTransform + + return 'foobar'; + } + )); + $form = $builder->getForm(); + + $form->submit(array( + 'referenceCopy' => array(), // doesn't matter actually + )); + + // firstName can only be updated if setReferenceCopy() was called + $this->assertEquals('foobar', $author->getReferenceCopy()); + } + + public function testSubformAlwaysInsertsIntoArrays() + { + $ref1 = new Author(); + $ref2 = new Author(); + $author = array('referenceCopy' => $ref1); + + $builder = $this->factory->createBuilder('form'); + $builder->setData($author); + $builder->add('referenceCopy', 'form'); + $builder->get('referenceCopy')->addViewTransformer(new CallbackTransformer( + function () {}, + function ($value) use ($ref2) { // reverseTransform + + return $ref2; + } + )); + $form = $builder->getForm(); + + $form->submit(array( + 'referenceCopy' => array('a' => 'b'), // doesn't matter actually + )); + + // the new reference was inserted into the array + $author = $form->getData(); + $this->assertSame($ref2, $author['referenceCopy']); + } + + public function testPassMultipartTrueIfAnyChildIsMultipartToView() + { + $view = $this->factory->createBuilder('form') + ->add('foo', 'text') + ->add('bar', 'file') + ->getForm() + ->createView(); + + $this->assertTrue($view->vars['multipart']); + } + + public function testViewIsNotRenderedByDefault() + { + $view = $this->factory->createBuilder('form') + ->add('foo', 'form') + ->getForm() + ->createView(); + + $this->assertFalse($view->isRendered()); + } + + public function testErrorBubblingIfCompound() + { + $form = $this->factory->create('form', null, array( + 'compound' => true, + )); + + $this->assertTrue($form->getConfig()->getErrorBubbling()); + } + + public function testNoErrorBubblingIfNotCompound() + { + $form = $this->factory->create('form', null, array( + 'compound' => false, + )); + + $this->assertFalse($form->getConfig()->getErrorBubbling()); + } + + public function testOverrideErrorBubbling() + { + $form = $this->factory->create('form', null, array( + 'compound' => false, + 'error_bubbling' => true, + )); + + $this->assertTrue($form->getConfig()->getErrorBubbling()); + } + + public function testPropertyPath() + { + $form = $this->factory->create('form', null, array( + 'property_path' => 'foo', + )); + + $this->assertEquals(new PropertyPath('foo'), $form->getPropertyPath()); + $this->assertTrue($form->getConfig()->getMapped()); + } + + public function testPropertyPathNullImpliesDefault() + { + $form = $this->factory->createNamed('name', 'form', null, array( + 'property_path' => null, + )); + + $this->assertEquals(new PropertyPath('name'), $form->getPropertyPath()); + $this->assertTrue($form->getConfig()->getMapped()); + } + + public function testNotMapped() + { + $form = $this->factory->create('form', null, array( + 'property_path' => 'foo', + 'mapped' => false, + )); + + $this->assertEquals(new PropertyPath('foo'), $form->getPropertyPath()); + $this->assertFalse($form->getConfig()->getMapped()); + } + + public function testViewValidNotSubmitted() + { + $form = $this->factory->create('form'); + $view = $form->createView(); + $this->assertTrue($view->vars['valid']); + } + + public function testViewNotValidSubmitted() + { + $form = $this->factory->create('form'); + $form->submit(array()); + $form->addError(new FormError('An error')); + $view = $form->createView(); + $this->assertFalse($view->vars['valid']); + } + + public function testDataOptionSupersedesSetDataCalls() + { + $form = $this->factory->create('form', null, array( + 'data' => 'default', + 'compound' => false, + )); + + $form->setData('foobar'); + + $this->assertSame('default', $form->getData()); + } + + public function testNormDataIsPassedToView() + { + $view = $this->factory->createBuilder('form') + ->addViewTransformer(new FixedDataTransformer(array( + 'foo' => 'bar', + ))) + ->setData('foo') + ->getForm() + ->createView(); + + $this->assertSame('foo', $view->vars['data']); + $this->assertSame('bar', $view->vars['value']); + } + + // https://github.com/symfony/symfony/issues/6862 + public function testPassZeroLabelToView() + { + $view = $this->factory->create('form', null, array( + 'label' => '0' + )) + ->createView(); + + $this->assertSame('0', $view->vars['label']); + } + + protected function getTestedType() + { + return 'form'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php new file mode 100644 index 00000000..998bbe01 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Intl\Util\IntlTestHelper; + +class IntegerTypeTest extends TypeTestCase +{ + protected function setUp() + { + IntlTestHelper::requireIntl($this); + + parent::setUp(); + } + + public function testSubmitCastsToInteger() + { + $form = $this->factory->create('integer'); + + $form->submit('1.678'); + + $this->assertSame(1, $form->getData()); + $this->assertSame('1', $form->getViewData()); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/LanguageTypeTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/LanguageTypeTest.php new file mode 100644 index 00000000..24434b21 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/LanguageTypeTest.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Extension\Core\View\ChoiceView; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class LanguageTypeTest extends TypeTestCase +{ + protected function setUp() + { + IntlTestHelper::requireIntl($this); + + parent::setUp(); + } + + public function testCountriesAreSelectable() + { + $form = $this->factory->create('language'); + $view = $form->createView(); + $choices = $view->vars['choices']; + + $this->assertContains(new ChoiceView('en', 'en', 'English'), $choices, '', false, false); + $this->assertContains(new ChoiceView('en_GB', 'en_GB', 'British English'), $choices, '', false, false); + $this->assertContains(new ChoiceView('en_US', 'en_US', 'U.S. English'), $choices, '', false, false); + $this->assertContains(new ChoiceView('fr', 'fr', 'French'), $choices, '', false, false); + $this->assertContains(new ChoiceView('my', 'my', 'Burmese'), $choices, '', false, false); + } + + public function testMultipleLanguagesIsNotIncluded() + { + $form = $this->factory->create('language', 'language'); + $view = $form->createView(); + $choices = $view->vars['choices']; + + $this->assertNotContains(new ChoiceView('mul', 'mul', 'Mehrsprachig'), $choices, '', false, false); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/LocaleTypeTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/LocaleTypeTest.php new file mode 100644 index 00000000..e402cb4b --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/LocaleTypeTest.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Extension\Core\View\ChoiceView; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class LocaleTypeTest extends TypeTestCase +{ + protected function setUp() + { + IntlTestHelper::requireIntl($this); + + parent::setUp(); + } + + public function testLocalesAreSelectable() + { + $form = $this->factory->create('locale'); + $view = $form->createView(); + $choices = $view->vars['choices']; + + $this->assertContains(new ChoiceView('en', 'en', 'English'), $choices, '', false, false); + $this->assertContains(new ChoiceView('en_GB', 'en_GB', 'English (United Kingdom)'), $choices, '', false, false); + $this->assertContains(new ChoiceView('zh_Hant_MO', 'zh_Hant_MO', 'Chinese (Traditional, Macau SAR China)'), $choices, '', false, false); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/MoneyTypeTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/MoneyTypeTest.php new file mode 100644 index 00000000..97fc37fa --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/MoneyTypeTest.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Intl\Util\IntlTestHelper; + +class MoneyTypeTest extends TypeTestCase +{ + protected function setUp() + { + // we test against different locales, so we need the full + // implementation + IntlTestHelper::requireFullIntl($this); + + parent::setUp(); + } + + public function testPassMoneyPatternToView() + { + \Locale::setDefault('de_DE'); + + $form = $this->factory->create('money'); + $view = $form->createView(); + + $this->assertSame('{{ widget }} €', $view->vars['money_pattern']); + } + + public function testMoneyPatternWorksForYen() + { + \Locale::setDefault('en_US'); + + $form = $this->factory->create('money', null, array('currency' => 'JPY')); + $view = $form->createView(); + $this->assertTrue((Boolean) strstr($view->vars['money_pattern'], '¥')); + } + + // https://github.com/symfony/symfony/issues/5458 + public function testPassDifferentPatternsForDifferentCurrencies() + { + \Locale::setDefault('de_DE'); + + $form1 = $this->factory->create('money', null, array('currency' => 'GBP')); + $form2 = $this->factory->create('money', null, array('currency' => 'EUR')); + $view1 = $form1->createView(); + $view2 = $form2->createView(); + + $this->assertSame('{{ widget }} £', $view1->vars['money_pattern']); + $this->assertSame('{{ widget }} €', $view2->vars['money_pattern']); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php new file mode 100644 index 00000000..e5b3dd74 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Intl\Util\IntlTestHelper; + +class NumberTypeTest extends TypeTestCase +{ + protected function setUp() + { + parent::setUp(); + + // we test against "de_DE", so we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_DE'); + } + + public function testDefaultFormatting() + { + $form = $this->factory->create('number'); + $form->setData('12345.67890'); + $view = $form->createView(); + + $this->assertSame('12345,679', $view->vars['value']); + } + + public function testDefaultFormattingWithGrouping() + { + $form = $this->factory->create('number', null, array('grouping' => true)); + $form->setData('12345.67890'); + $view = $form->createView(); + + $this->assertSame('12.345,679', $view->vars['value']); + } + + public function testDefaultFormattingWithPrecision() + { + $form = $this->factory->create('number', null, array('precision' => 2)); + $form->setData('12345.67890'); + $view = $form->createView(); + + $this->assertSame('12345,68', $view->vars['value']); + } + + public function testDefaultFormattingWithRounding() + { + $form = $this->factory->create('number', null, array('precision' => 0, 'rounding_mode' => \NumberFormatter::ROUND_UP)); + $form->setData('12345.54321'); + $view = $form->createView(); + + $this->assertSame('12346', $view->vars['value']); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/PasswordTypeTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/PasswordTypeTest.php new file mode 100644 index 00000000..bccb6f7b --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/PasswordTypeTest.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +class PasswordTypeTest extends \Symfony\Component\Form\Test\TypeTestCase +{ + public function testEmptyIfNotSubmitted() + { + $form = $this->factory->create('password'); + $form->setData('pAs5w0rd'); + $view = $form->createView(); + + $this->assertSame('', $view->vars['value']); + } + + public function testEmptyIfSubmitted() + { + $form = $this->factory->create('password'); + $form->submit('pAs5w0rd'); + $view = $form->createView(); + + $this->assertSame('', $view->vars['value']); + } + + public function testNotEmptyIfSubmittedAndNotAlwaysEmpty() + { + $form = $this->factory->create('password', null, array('always_empty' => false)); + $form->submit('pAs5w0rd'); + $view = $form->createView(); + + $this->assertSame('pAs5w0rd', $view->vars['value']); + } + + public function testNotTrimmed() + { + $form = $this->factory->create('password', null); + $form->submit(' pAs5w0rd '); + $data = $form->getData(); + + $this->assertSame(' pAs5w0rd ', $data); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php new file mode 100644 index 00000000..9e125d78 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php @@ -0,0 +1,149 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +class RepeatedTypeTest extends \Symfony\Component\Form\Test\TypeTestCase +{ + protected $form; + + protected function setUp() + { + parent::setUp(); + + $this->form = $this->factory->create('repeated', null, array( + 'type' => 'text', + )); + $this->form->setData(null); + } + + public function testSetData() + { + $this->form->setData('foobar'); + + $this->assertEquals('foobar', $this->form['first']->getData()); + $this->assertEquals('foobar', $this->form['second']->getData()); + } + + public function testSetOptions() + { + $form = $this->factory->create('repeated', null, array( + 'type' => 'text', + 'options' => array('label' => 'Global'), + )); + + $this->assertEquals('Global', $form['first']->getConfig()->getOption('label')); + $this->assertEquals('Global', $form['second']->getConfig()->getOption('label')); + $this->assertTrue($form['first']->isRequired()); + $this->assertTrue($form['second']->isRequired()); + } + + public function testSetOptionsPerChild() + { + $form = $this->factory->create('repeated', null, array( + // the global required value cannot be overridden + 'type' => 'text', + 'first_options' => array('label' => 'Test', 'required' => false), + 'second_options' => array('label' => 'Test2') + )); + + $this->assertEquals('Test', $form['first']->getConfig()->getOption('label')); + $this->assertEquals('Test2', $form['second']->getConfig()->getOption('label')); + $this->assertTrue($form['first']->isRequired()); + $this->assertTrue($form['second']->isRequired()); + } + + public function testSetRequired() + { + $form = $this->factory->create('repeated', null, array( + 'required' => false, + 'type' => 'text', + )); + + $this->assertFalse($form['first']->isRequired()); + $this->assertFalse($form['second']->isRequired()); + } + + public function testSetErrorBubblingToTrue() + { + $form = $this->factory->create('repeated', null, array( + 'error_bubbling' => true, + )); + + $this->assertTrue($form->getConfig()->getOption('error_bubbling')); + $this->assertTrue($form['first']->getConfig()->getOption('error_bubbling')); + $this->assertTrue($form['second']->getConfig()->getOption('error_bubbling')); + } + + public function testSetErrorBubblingToFalse() + { + $form = $this->factory->create('repeated', null, array( + 'error_bubbling' => false, + )); + + $this->assertFalse($form->getConfig()->getOption('error_bubbling')); + $this->assertFalse($form['first']->getConfig()->getOption('error_bubbling')); + $this->assertFalse($form['second']->getConfig()->getOption('error_bubbling')); + } + + public function testSetErrorBubblingIndividually() + { + $form = $this->factory->create('repeated', null, array( + 'error_bubbling' => true, + 'options' => array('error_bubbling' => false), + 'second_options' => array('error_bubbling' => true), + )); + + $this->assertTrue($form->getConfig()->getOption('error_bubbling')); + $this->assertFalse($form['first']->getConfig()->getOption('error_bubbling')); + $this->assertTrue($form['second']->getConfig()->getOption('error_bubbling')); + } + + public function testSetOptionsPerChildAndOverwrite() + { + $form = $this->factory->create('repeated', null, array( + 'type' => 'text', + 'options' => array('label' => 'Label'), + 'second_options' => array('label' => 'Second label') + )); + + $this->assertEquals('Label', $form['first']->getConfig()->getOption('label')); + $this->assertEquals('Second label', $form['second']->getConfig()->getOption('label')); + $this->assertTrue($form['first']->isRequired()); + $this->assertTrue($form['second']->isRequired()); + } + + public function testSubmitUnequal() + { + $input = array('first' => 'foo', 'second' => 'bar'); + + $this->form->submit($input); + + $this->assertEquals('foo', $this->form['first']->getViewData()); + $this->assertEquals('bar', $this->form['second']->getViewData()); + $this->assertFalse($this->form->isSynchronized()); + $this->assertEquals($input, $this->form->getViewData()); + $this->assertNull($this->form->getData()); + } + + public function testSubmitEqual() + { + $input = array('first' => 'foo', 'second' => 'foo'); + + $this->form->submit($input); + + $this->assertEquals('foo', $this->form['first']->getViewData()); + $this->assertEquals('foo', $this->form['second']->getViewData()); + $this->assertTrue($this->form->isSynchronized()); + $this->assertEquals($input, $this->form->getViewData()); + $this->assertEquals('foo', $this->form->getData()); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/SubmitTypeTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/SubmitTypeTest.php new file mode 100644 index 00000000..8cc72281 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/SubmitTypeTest.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +/** + * @author Bernhard Schussek + */ +class SubmitTypeTest extends TypeTestCase +{ + public function testCreateSubmitButtonInstances() + { + $this->assertInstanceOf('Symfony\Component\Form\SubmitButton', $this->factory->create('submit')); + } + + public function testNotClickedByDefault() + { + $button = $this->factory->create('submit'); + + $this->assertFalse($button->isClicked()); + } + + public function testNotClickedIfSubmittedWithNull() + { + $button = $this->factory->create('submit'); + $button->submit(null); + + $this->assertFalse($button->isClicked()); + } + + public function testClickedIfSubmittedWithEmptyString() + { + $button = $this->factory->create('submit'); + $button->submit(''); + + $this->assertTrue($button->isClicked()); + } + + public function testClickedIfSubmittedWithUnemptyString() + { + $button = $this->factory->create('submit'); + $button->submit('foo'); + + $this->assertTrue($button->isClicked()); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php new file mode 100644 index 00000000..9bdfe156 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php @@ -0,0 +1,649 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Extension\Core\View\ChoiceView; +use Symfony\Component\Form\FormError; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class TimeTypeTest extends TypeTestCase +{ + protected function setUp() + { + IntlTestHelper::requireIntl($this); + + parent::setUp(); + } + + public function testSubmitDateTime() + { + $form = $this->factory->create('time', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'datetime', + )); + + $input = array( + 'hour' => '3', + 'minute' => '4', + ); + + $form->submit($input); + + $dateTime = new \DateTime('1970-01-01 03:04:00 UTC'); + + $this->assertEquals($dateTime, $form->getData()); + $this->assertEquals($input, $form->getViewData()); + } + + public function testSubmitString() + { + $form = $this->factory->create('time', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'string', + )); + + $input = array( + 'hour' => '3', + 'minute' => '4', + ); + + $form->submit($input); + + $this->assertEquals('03:04:00', $form->getData()); + $this->assertEquals($input, $form->getViewData()); + } + + public function testSubmitTimestamp() + { + $form = $this->factory->create('time', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'timestamp', + )); + + $input = array( + 'hour' => '3', + 'minute' => '4', + ); + + $form->submit($input); + + $dateTime = new \DateTime('1970-01-01 03:04:00 UTC'); + + $this->assertEquals($dateTime->format('U'), $form->getData()); + $this->assertEquals($input, $form->getViewData()); + } + + public function testSubmitArray() + { + $form = $this->factory->create('time', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'array', + )); + + $input = array( + 'hour' => '3', + 'minute' => '4', + ); + + $form->submit($input); + + $this->assertEquals($input, $form->getData()); + $this->assertEquals($input, $form->getViewData()); + } + + public function testSubmitDatetimeSingleText() + { + $form = $this->factory->create('time', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'datetime', + 'widget' => 'single_text', + )); + + $form->submit('03:04'); + + $this->assertEquals(new \DateTime('1970-01-01 03:04:00 UTC'), $form->getData()); + $this->assertEquals('03:04', $form->getViewData()); + } + + public function testSubmitDatetimeSingleTextWithoutMinutes() + { + $form = $this->factory->create('time', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'datetime', + 'widget' => 'single_text', + 'with_minutes' => false, + )); + + $form->submit('03'); + + $this->assertEquals(new \DateTime('1970-01-01 03:00:00 UTC'), $form->getData()); + $this->assertEquals('03', $form->getViewData()); + } + + public function testSubmitArraySingleText() + { + $form = $this->factory->create('time', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'array', + 'widget' => 'single_text', + )); + + $data = array( + 'hour' => '3', + 'minute' => '4', + ); + + $form->submit('03:04'); + + $this->assertEquals($data, $form->getData()); + $this->assertEquals('03:04', $form->getViewData()); + } + + public function testSubmitArraySingleTextWithoutMinutes() + { + $form = $this->factory->create('time', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'array', + 'widget' => 'single_text', + 'with_minutes' => false, + )); + + $data = array( + 'hour' => '3', + ); + + $form->submit('03'); + + $this->assertEquals($data, $form->getData()); + $this->assertEquals('03', $form->getViewData()); + } + + public function testSubmitArraySingleTextWithSeconds() + { + $form = $this->factory->create('time', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'array', + 'widget' => 'single_text', + 'with_seconds' => true, + )); + + $data = array( + 'hour' => '3', + 'minute' => '4', + 'second' => '5', + ); + + $form->submit('03:04:05'); + + $this->assertEquals($data, $form->getData()); + $this->assertEquals('03:04:05', $form->getViewData()); + } + + public function testSubmitStringSingleText() + { + $form = $this->factory->create('time', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'string', + 'widget' => 'single_text', + )); + + $form->submit('03:04'); + + $this->assertEquals('03:04:00', $form->getData()); + $this->assertEquals('03:04', $form->getViewData()); + } + + public function testSubmitStringSingleTextWithoutMinutes() + { + $form = $this->factory->create('time', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'string', + 'widget' => 'single_text', + 'with_minutes' => false, + )); + + $form->submit('03'); + + $this->assertEquals('03:00:00', $form->getData()); + $this->assertEquals('03', $form->getViewData()); + } + + public function testSetDataWithoutMinutes() + { + $form = $this->factory->create('time', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'datetime', + 'with_minutes' => false, + )); + + $form->setData(new \DateTime('03:04:05 UTC')); + + $this->assertEquals(array('hour' => 3), $form->getViewData()); + } + + public function testSetDataWithSeconds() + { + $form = $this->factory->create('time', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'datetime', + 'with_seconds' => true, + )); + + $form->setData(new \DateTime('03:04:05 UTC')); + + $this->assertEquals(array('hour' => 3, 'minute' => 4, 'second' => 5), $form->getViewData()); + } + + public function testSetDataDifferentTimezones() + { + $form = $this->factory->create('time', null, array( + 'model_timezone' => 'America/New_York', + 'view_timezone' => 'Asia/Hong_Kong', + 'input' => 'string', + 'with_seconds' => true, + )); + + $dateTime = new \DateTime('2013-01-01 12:04:05'); + $dateTime->setTimezone(new \DateTimeZone('America/New_York')); + + $form->setData($dateTime->format('H:i:s')); + + $outputTime = clone $dateTime; + $outputTime->setTimezone(new \DateTimeZone('Asia/Hong_Kong')); + + $displayedData = array( + 'hour' => (int) $outputTime->format('H'), + 'minute' => (int) $outputTime->format('i'), + 'second' => (int) $outputTime->format('s') + ); + + $this->assertEquals($displayedData, $form->getViewData()); + } + + public function testSetDataDifferentTimezonesDateTime() + { + $form = $this->factory->create('time', null, array( + 'model_timezone' => 'America/New_York', + 'view_timezone' => 'Asia/Hong_Kong', + 'input' => 'datetime', + 'with_seconds' => true, + )); + + $dateTime = new \DateTime('12:04:05'); + $dateTime->setTimezone(new \DateTimeZone('America/New_York')); + + $form->setData($dateTime); + + $outputTime = clone $dateTime; + $outputTime->setTimezone(new \DateTimeZone('Asia/Hong_Kong')); + + $displayedData = array( + 'hour' => (int) $outputTime->format('H'), + 'minute' => (int) $outputTime->format('i'), + 'second' => (int) $outputTime->format('s') + ); + + $this->assertDateTimeEquals($dateTime, $form->getData()); + $this->assertEquals($displayedData, $form->getViewData()); + } + + public function testHoursOption() + { + $form = $this->factory->create('time', null, array( + 'hours' => array(6, 7), + )); + + $view = $form->createView(); + + $this->assertEquals(array( + new ChoiceView('6', '6', '06'), + new ChoiceView('7', '7', '07'), + ), $view['hour']->vars['choices']); + } + + public function testIsMinuteWithinRangeReturnsTrueIfWithin() + { + $form = $this->factory->create('time', null, array( + 'minutes' => array(6, 7), + )); + + $view = $form->createView(); + + $this->assertEquals(array( + new ChoiceView('6', '6', '06'), + new ChoiceView('7', '7', '07'), + ), $view['minute']->vars['choices']); + } + + public function testIsSecondWithinRangeReturnsTrueIfWithin() + { + $form = $this->factory->create('time', null, array( + 'seconds' => array(6, 7), + 'with_seconds' => true, + )); + + $view = $form->createView(); + + $this->assertEquals(array( + new ChoiceView('6', '6', '06'), + new ChoiceView('7', '7', '07'), + ), $view['second']->vars['choices']); + } + + public function testIsPartiallyFilledReturnsFalseIfCompletelyEmpty() + { + $this->markTestIncomplete('Needs to be reimplemented using validators'); + + $form = $this->factory->create('time', null, array( + 'widget' => 'choice', + )); + + $form->submit(array( + 'hour' => '', + 'minute' => '', + )); + + $this->assertFalse($form->isPartiallyFilled()); + } + + public function testIsPartiallyFilledReturnsFalseIfCompletelyEmptyWithSeconds() + { + $this->markTestIncomplete('Needs to be reimplemented using validators'); + + $form = $this->factory->create('time', null, array( + 'widget' => 'choice', + 'with_seconds' => true, + )); + + $form->submit(array( + 'hour' => '', + 'minute' => '', + 'second' => '', + )); + + $this->assertFalse($form->isPartiallyFilled()); + } + + public function testIsPartiallyFilledReturnsFalseIfCompletelyFilled() + { + $this->markTestIncomplete('Needs to be reimplemented using validators'); + + $form = $this->factory->create('time', null, array( + 'widget' => 'choice', + )); + + $form->submit(array( + 'hour' => '0', + 'minute' => '0', + )); + + $this->assertFalse($form->isPartiallyFilled()); + } + + public function testIsPartiallyFilledReturnsFalseIfCompletelyFilledWithSeconds() + { + $this->markTestIncomplete('Needs to be reimplemented using validators'); + + $form = $this->factory->create('time', null, array( + 'widget' => 'choice', + 'with_seconds' => true, + )); + + $form->submit(array( + 'hour' => '0', + 'minute' => '0', + 'second' => '0', + )); + + $this->assertFalse($form->isPartiallyFilled()); + } + + public function testIsPartiallyFilledReturnsTrueIfChoiceAndHourEmpty() + { + $this->markTestIncomplete('Needs to be reimplemented using validators'); + + $form = $this->factory->create('time', null, array( + 'widget' => 'choice', + 'with_seconds' => true, + )); + + $form->submit(array( + 'hour' => '', + 'minute' => '0', + 'second' => '0', + )); + + $this->assertTrue($form->isPartiallyFilled()); + } + + public function testIsPartiallyFilledReturnsTrueIfChoiceAndMinuteEmpty() + { + $this->markTestIncomplete('Needs to be reimplemented using validators'); + + $form = $this->factory->create('time', null, array( + 'widget' => 'choice', + 'with_seconds' => true, + )); + + $form->submit(array( + 'hour' => '0', + 'minute' => '', + 'second' => '0', + )); + + $this->assertTrue($form->isPartiallyFilled()); + } + + public function testIsPartiallyFilledReturnsTrueIfChoiceAndSecondsEmpty() + { + $this->markTestIncomplete('Needs to be reimplemented using validators'); + + $form = $this->factory->create('time', null, array( + 'widget' => 'choice', + 'with_seconds' => true, + )); + + $form->submit(array( + 'hour' => '0', + 'minute' => '0', + 'second' => '', + )); + + $this->assertTrue($form->isPartiallyFilled()); + } + + // Bug fix + public function testInitializeWithDateTime() + { + // Throws an exception if "data_class" option is not explicitly set + // to null in the type + $this->factory->create('time', new \DateTime()); + } + + public function testSingleTextWidgetShouldUseTheRightInputType() + { + $form = $this->factory->create('time', null, array( + 'widget' => 'single_text', + )); + + $view = $form->createView(); + $this->assertEquals('time', $view->vars['type']); + } + + public function testPassDefaultEmptyValueToViewIfNotRequired() + { + $form = $this->factory->create('time', null, array( + 'required' => false, + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('', $view['hour']->vars['empty_value']); + $this->assertSame('', $view['minute']->vars['empty_value']); + $this->assertSame('', $view['second']->vars['empty_value']); + } + + public function testPassNoEmptyValueToViewIfRequired() + { + $form = $this->factory->create('time', null, array( + 'required' => true, + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertNull($view['hour']->vars['empty_value']); + $this->assertNull($view['minute']->vars['empty_value']); + $this->assertNull($view['second']->vars['empty_value']); + } + + public function testPassEmptyValueAsString() + { + $form = $this->factory->create('time', null, array( + 'empty_value' => 'Empty', + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('Empty', $view['hour']->vars['empty_value']); + $this->assertSame('Empty', $view['minute']->vars['empty_value']); + $this->assertSame('Empty', $view['second']->vars['empty_value']); + } + + public function testPassEmptyValueAsArray() + { + $form = $this->factory->create('time', null, array( + 'empty_value' => array( + 'hour' => 'Empty hour', + 'minute' => 'Empty minute', + 'second' => 'Empty second', + ), + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('Empty hour', $view['hour']->vars['empty_value']); + $this->assertSame('Empty minute', $view['minute']->vars['empty_value']); + $this->assertSame('Empty second', $view['second']->vars['empty_value']); + } + + public function testPassEmptyValueAsPartialArrayAddEmptyIfNotRequired() + { + $form = $this->factory->create('time', null, array( + 'required' => false, + 'empty_value' => array( + 'hour' => 'Empty hour', + 'second' => 'Empty second', + ), + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('Empty hour', $view['hour']->vars['empty_value']); + $this->assertSame('', $view['minute']->vars['empty_value']); + $this->assertSame('Empty second', $view['second']->vars['empty_value']); + } + + public function testPassEmptyValueAsPartialArrayAddNullIfRequired() + { + $form = $this->factory->create('time', null, array( + 'required' => true, + 'empty_value' => array( + 'hour' => 'Empty hour', + 'second' => 'Empty second', + ), + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('Empty hour', $view['hour']->vars['empty_value']); + $this->assertNull($view['minute']->vars['empty_value']); + $this->assertSame('Empty second', $view['second']->vars['empty_value']); + } + + public function provideCompoundWidgets() + { + return array( + array('text'), + array('choice'), + ); + } + + /** + * @dataProvider provideCompoundWidgets + */ + public function testHourErrorsBubbleUp($widget) + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('time', null, array( + 'widget' => $widget, + )); + $form['hour']->addError($error); + + $this->assertSame(array(), $form['hour']->getErrors()); + $this->assertSame(array($error), $form->getErrors()); + } + + /** + * @dataProvider provideCompoundWidgets + */ + public function testMinuteErrorsBubbleUp($widget) + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('time', null, array( + 'widget' => $widget, + )); + $form['minute']->addError($error); + + $this->assertSame(array(), $form['minute']->getErrors()); + $this->assertSame(array($error), $form->getErrors()); + } + + /** + * @dataProvider provideCompoundWidgets + */ + public function testSecondErrorsBubbleUp($widget) + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('time', null, array( + 'widget' => $widget, + 'with_seconds' => true, + )); + $form['second']->addError($error); + + $this->assertSame(array(), $form['second']->getErrors()); + $this->assertSame(array($error), $form->getErrors()); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\InvalidConfigurationException + */ + public function testInitializeWithSecondsAndWithoutMinutes() + { + $this->factory->create('time', null, array( + 'with_minutes' => false, + 'with_seconds' => true, + )); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/TimezoneTypeTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/TimezoneTypeTest.php new file mode 100644 index 00000000..81df20cb --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/TimezoneTypeTest.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Extension\Core\View\ChoiceView; + +class TimezoneTypeTest extends \Symfony\Component\Form\Test\TypeTestCase +{ + public function testTimezonesAreSelectable() + { + $form = $this->factory->create('timezone'); + $view = $form->createView(); + $choices = $view->vars['choices']; + + $this->assertArrayHasKey('Africa', $choices); + $this->assertContains(new ChoiceView('Africa/Kinshasa', 'Africa/Kinshasa', 'Kinshasa'), $choices['Africa'], '', false, false); + + $this->assertArrayHasKey('America', $choices); + $this->assertContains(new ChoiceView('America/New_York', 'America/New_York', 'New York'), $choices['America'], '', false, false); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/TypeTestCase.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/TypeTestCase.php new file mode 100644 index 00000000..733546e3 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/TypeTestCase.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Test\TypeTestCase as BaseTypeTestCase; + +/** + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use Symfony\Component\Form\Test\TypeTestCase instead. + */ +abstract class TypeTestCase extends BaseTypeTestCase +{ +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/UrlTypeTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/UrlTypeTest.php new file mode 100644 index 00000000..254b2a8e --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/UrlTypeTest.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +class UrlTypeTest extends TypeTestCase +{ + public function testSubmitAddsDefaultProtocolIfNoneIsIncluded() + { + $form = $this->factory->create('url', 'name'); + + $form->submit('www.domain.com'); + + $this->assertSame('http://www.domain.com', $form->getData()); + $this->assertSame('http://www.domain.com', $form->getViewData()); + } + + public function testSubmitAddsNoDefaultProtocolIfAlreadyIncluded() + { + $form = $this->factory->create('url', null, array( + 'default_protocol' => 'http', + )); + + $form->submit('ftp://www.domain.com'); + + $this->assertSame('ftp://www.domain.com', $form->getData()); + $this->assertSame('ftp://www.domain.com', $form->getViewData()); + } + + public function testSubmitAddsNoDefaultProtocolIfEmpty() + { + $form = $this->factory->create('url', null, array( + 'default_protocol' => 'http', + )); + + $form->submit(''); + + $this->assertNull($form->getData()); + $this->assertSame('', $form->getViewData()); + } + + public function testSubmitAddsNoDefaultProtocolIfSetToNull() + { + $form = $this->factory->create('url', null, array( + 'default_protocol' => null, + )); + + $form->submit('www.domain.com'); + + $this->assertSame('www.domain.com', $form->getData()); + $this->assertSame('www.domain.com', $form->getViewData()); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Csrf/CsrfProvider/DefaultCsrfProviderTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Csrf/CsrfProvider/DefaultCsrfProviderTest.php new file mode 100644 index 00000000..a99b5444 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Csrf/CsrfProvider/DefaultCsrfProviderTest.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Csrf\CsrfProvider; + +use Symfony\Component\Form\Extension\Csrf\CsrfProvider\DefaultCsrfProvider; + +/** + * @runTestsInSeparateProcesses + */ +class DefaultCsrfProviderTest extends \PHPUnit_Framework_TestCase +{ + protected $provider; + + public static function setUpBeforeClass() + { + ini_set('session.save_handler', 'files'); + ini_set('session.save_path', sys_get_temp_dir()); + } + + protected function setUp() + { + $this->provider = new DefaultCsrfProvider('SECRET'); + } + + protected function tearDown() + { + $this->provider = null; + } + + public function testGenerateCsrfToken() + { + session_start(); + + $token = $this->provider->generateCsrfToken('foo'); + + $this->assertEquals(sha1('SECRET'.'foo'.session_id()), $token); + } + + public function testGenerateCsrfTokenOnUnstartedSession() + { + session_id('touti'); + + if (!version_compare(PHP_VERSION, '5.4', '>=')) { + $this->markTestSkipped('This test requires PHP >= 5.4'); + } + + $this->assertSame(PHP_SESSION_NONE, session_status()); + + $token = $this->provider->generateCsrfToken('foo'); + + $this->assertEquals(sha1('SECRET'.'foo'.session_id()), $token); + $this->assertSame(PHP_SESSION_ACTIVE, session_status()); + } + + public function testIsCsrfTokenValidSucceeds() + { + session_start(); + + $token = sha1('SECRET'.'foo'.session_id()); + + $this->assertTrue($this->provider->isCsrfTokenValid('foo', $token)); + } + + public function testIsCsrfTokenValidFails() + { + session_start(); + + $token = sha1('SECRET'.'bar'.session_id()); + + $this->assertFalse($this->provider->isCsrfTokenValid('foo', $token)); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Csrf/CsrfProvider/SessionCsrfProviderTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Csrf/CsrfProvider/SessionCsrfProviderTest.php new file mode 100644 index 00000000..1dcc6b4c --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Csrf/CsrfProvider/SessionCsrfProviderTest.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Csrf\CsrfProvider; + +use Symfony\Component\Form\Extension\Csrf\CsrfProvider\SessionCsrfProvider; + +class SessionCsrfProviderTest extends \PHPUnit_Framework_TestCase +{ + protected $provider; + protected $session; + + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Session\Session')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $this->session = $this->getMock( + 'Symfony\Component\HttpFoundation\Session\Session', + array(), + array(), + '', + false // don't call constructor + ); + $this->provider = new SessionCsrfProvider($this->session, 'SECRET'); + } + + protected function tearDown() + { + $this->provider = null; + $this->session = null; + } + + public function testGenerateCsrfToken() + { + $this->session->expects($this->once()) + ->method('getId') + ->will($this->returnValue('ABCDEF')); + + $token = $this->provider->generateCsrfToken('foo'); + + $this->assertEquals(sha1('SECRET'.'foo'.'ABCDEF'), $token); + } + + public function testIsCsrfTokenValidSucceeds() + { + $this->session->expects($this->once()) + ->method('getId') + ->will($this->returnValue('ABCDEF')); + + $token = sha1('SECRET'.'foo'.'ABCDEF'); + + $this->assertTrue($this->provider->isCsrfTokenValid('foo', $token)); + } + + public function testIsCsrfTokenValidFails() + { + $this->session->expects($this->once()) + ->method('getId') + ->will($this->returnValue('ABCDEF')); + + $token = sha1('SECRET'.'bar'.'ABCDEF'); + + $this->assertFalse($this->provider->isCsrfTokenValid('foo', $token)); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/CsrfValidationListenerTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/CsrfValidationListenerTest.php new file mode 100644 index 00000000..0bcfe74e --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/CsrfValidationListenerTest.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Csrf\EventListener; + +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Extension\Csrf\EventListener\CsrfValidationListener; + +class CsrfValidationListenerTest extends \PHPUnit_Framework_TestCase +{ + protected $dispatcher; + protected $factory; + protected $csrfProvider; + + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); + $this->csrfProvider = $this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface'); + $this->form = $this->getBuilder('post') + ->setDataMapper($this->getDataMapper()) + ->getForm(); + } + + protected function tearDown() + { + $this->dispatcher = null; + $this->factory = null; + $this->csrfProvider = null; + $this->form = null; + } + + protected function getBuilder($name = 'name') + { + return new FormBuilder($name, null, $this->dispatcher, $this->factory, array('compound' => true)); + } + + protected function getForm($name = 'name') + { + return $this->getBuilder($name)->getForm(); + } + + protected function getDataMapper() + { + return $this->getMock('Symfony\Component\Form\DataMapperInterface'); + } + + protected function getMockForm() + { + return $this->getMock('Symfony\Component\Form\Test\FormInterface'); + } + + // https://github.com/symfony/symfony/pull/5838 + public function testStringFormData() + { + $data = "XP4HUzmHPi"; + $event = new FormEvent($this->form, $data); + + $validation = new CsrfValidationListener('csrf', $this->csrfProvider, 'unknown', 'Invalid.'); + $validation->preSubmit($event); + + // Validate accordingly + $this->assertSame($data, $event->getData()); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php new file mode 100644 index 00000000..0a1f0dc4 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php @@ -0,0 +1,301 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Csrf\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormError; +use Symfony\Component\Form\Test\TypeTestCase; +use Symfony\Component\Form\Extension\Csrf\CsrfExtension; + +class FormTypeCsrfExtensionTest_ChildType extends AbstractType +{ + public function buildForm(FormBuilderInterface $builder, array $options) + { + // The form needs a child in order to trigger CSRF protection by + // default + $builder->add('name', 'text'); + } + + public function getName() + { + return 'csrf_collection_test'; + } +} + +class FormTypeCsrfExtensionTest extends TypeTestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $csrfProvider; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $translator; + + protected function setUp() + { + $this->csrfProvider = $this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface'); + $this->translator = $this->getMock('Symfony\Component\Translation\TranslatorInterface'); + + parent::setUp(); + } + + protected function tearDown() + { + $this->csrfProvider = null; + $this->translator = null; + + parent::tearDown(); + } + + protected function getExtensions() + { + return array_merge(parent::getExtensions(), array( + new CsrfExtension($this->csrfProvider, $this->translator), + )); + } + + public function testCsrfProtectionByDefaultIfRootAndCompound() + { + $view = $this->factory + ->create('form', null, array( + 'csrf_field_name' => 'csrf', + 'compound' => true, + )) + ->createView(); + + $this->assertTrue(isset($view['csrf'])); + } + + public function testNoCsrfProtectionByDefaultIfCompoundButNotRoot() + { + $view = $this->factory + ->createNamedBuilder('root', 'form') + ->add($this->factory + ->createNamedBuilder('form', 'form', null, array( + 'csrf_field_name' => 'csrf', + 'compound' => true, + )) + ) + ->getForm() + ->get('form') + ->createView(); + + $this->assertFalse(isset($view['csrf'])); + } + + public function testNoCsrfProtectionByDefaultIfRootButNotCompound() + { + $view = $this->factory + ->create('form', null, array( + 'csrf_field_name' => 'csrf', + 'compound' => false, + )) + ->createView(); + + $this->assertFalse(isset($view['csrf'])); + } + + public function testCsrfProtectionCanBeDisabled() + { + $view = $this->factory + ->create('form', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_protection' => false, + 'compound' => true, + )) + ->createView(); + + $this->assertFalse(isset($view['csrf'])); + } + + public function testGenerateCsrfToken() + { + $this->csrfProvider->expects($this->once()) + ->method('generateCsrfToken') + ->with('%INTENTION%') + ->will($this->returnValue('token')); + + $view = $this->factory + ->create('form', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_provider' => $this->csrfProvider, + 'intention' => '%INTENTION%', + 'compound' => true, + )) + ->createView(); + + $this->assertEquals('token', $view['csrf']->vars['value']); + } + + public function provideBoolean() + { + return array( + array(true), + array(false), + ); + } + + /** + * @dataProvider provideBoolean + */ + public function testValidateTokenOnSubmitIfRootAndCompound($valid) + { + $this->csrfProvider->expects($this->once()) + ->method('isCsrfTokenValid') + ->with('%INTENTION%', 'token') + ->will($this->returnValue($valid)); + + $form = $this->factory + ->createBuilder('form', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_provider' => $this->csrfProvider, + 'intention' => '%INTENTION%', + 'compound' => true, + )) + ->add('child', 'text') + ->getForm(); + + $form->submit(array( + 'child' => 'foobar', + 'csrf' => 'token', + )); + + // Remove token from data + $this->assertSame(array('child' => 'foobar'), $form->getData()); + + // Validate accordingly + $this->assertSame($valid, $form->isValid()); + } + + public function testFailIfRootAndCompoundAndTokenMissing() + { + $this->csrfProvider->expects($this->never()) + ->method('isCsrfTokenValid'); + + $form = $this->factory + ->createBuilder('form', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_provider' => $this->csrfProvider, + 'intention' => '%INTENTION%', + 'compound' => true, + )) + ->add('child', 'text') + ->getForm(); + + $form->submit(array( + 'child' => 'foobar', + // token is missing + )); + + // Remove token from data + $this->assertSame(array('child' => 'foobar'), $form->getData()); + + // Validate accordingly + $this->assertFalse($form->isValid()); + } + + public function testDontValidateTokenIfCompoundButNoRoot() + { + $this->csrfProvider->expects($this->never()) + ->method('isCsrfTokenValid'); + + $form = $this->factory + ->createNamedBuilder('root', 'form') + ->add($this->factory + ->createNamedBuilder('form', 'form', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_provider' => $this->csrfProvider, + 'intention' => '%INTENTION%', + 'compound' => true, + )) + ) + ->getForm() + ->get('form'); + + $form->submit(array( + 'child' => 'foobar', + 'csrf' => 'token', + )); + } + + public function testDontValidateTokenIfRootButNotCompound() + { + $this->csrfProvider->expects($this->never()) + ->method('isCsrfTokenValid'); + + $form = $this->factory + ->create('form', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_provider' => $this->csrfProvider, + 'intention' => '%INTENTION%', + 'compound' => false, + )); + + $form->submit(array( + 'csrf' => 'token', + )); + } + + public function testNoCsrfProtectionOnPrototype() + { + $prototypeView = $this->factory + ->create('collection', null, array( + 'type' => new FormTypeCsrfExtensionTest_ChildType(), + 'options' => array( + 'csrf_field_name' => 'csrf', + ), + 'prototype' => true, + 'allow_add' => true, + )) + ->createView() + ->vars['prototype']; + + $this->assertFalse(isset($prototypeView['csrf'])); + $this->assertCount(1, $prototypeView); + } + + public function testsTranslateCustomErrorMessage() + { + $this->csrfProvider->expects($this->once()) + ->method('isCsrfTokenValid') + ->with('%INTENTION%', 'token') + ->will($this->returnValue(false)); + + $this->translator->expects($this->once()) + ->method('trans') + ->with('Foobar') + ->will($this->returnValue('[trans]Foobar[/trans]')); + + $form = $this->factory + ->createBuilder('form', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_provider' => $this->csrfProvider, + 'csrf_message' => 'Foobar', + 'intention' => '%INTENTION%', + 'compound' => true, + )) + ->getForm(); + + $form->submit(array( + 'csrf' => 'token', + )); + + $errors = $form->getErrors(); + + $this->assertGreaterThan(0, count($errors)); + $this->assertEquals(new FormError('[trans]Foobar[/trans]'), $errors[0]); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/HttpFoundation/EventListener/BindRequestListenerTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/HttpFoundation/EventListener/BindRequestListenerTest.php new file mode 100644 index 00000000..2ff072b2 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/HttpFoundation/EventListener/BindRequestListenerTest.php @@ -0,0 +1,286 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\HttpFoundation\EventListener; + +use Symfony\Component\Form\Extension\HttpFoundation\EventListener\BindRequestListener; +use Symfony\Component\Form\Form; +use Symfony\Component\Form\FormConfigBuilder; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Test\DeprecationErrorHandler; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\File\UploadedFile; + +/** + * @author Bernhard Schussek + */ +class BindRequestListenerTest extends \PHPUnit_Framework_TestCase +{ + private $values; + + private $filesPlain; + + private $filesNested; + + /** + * @var UploadedFile + */ + private $uploadedFile; + + protected function setUp() + { + $path = tempnam(sys_get_temp_dir(), 'sf2'); + touch($path); + + $this->values = array( + 'name' => 'Bernhard', + 'image' => array('filename' => 'foobar.png'), + ); + + $this->filesPlain = array( + 'image' => array( + 'error' => UPLOAD_ERR_OK, + 'name' => 'upload.png', + 'size' => 123, + 'tmp_name' => $path, + 'type' => 'image/png' + ), + ); + + $this->filesNested = array( + 'error' => array('image' => UPLOAD_ERR_OK), + 'name' => array('image' => 'upload.png'), + 'size' => array('image' => 123), + 'tmp_name' => array('image' => $path), + 'type' => array('image' => 'image/png'), + ); + + $this->uploadedFile = new UploadedFile($path, 'upload.png', 'image/png', 123, UPLOAD_ERR_OK); + } + + protected function tearDown() + { + unlink($this->uploadedFile->getRealPath()); + } + + public function requestMethodProvider() + { + return array( + array('POST'), + array('PUT'), + array('DELETE'), + array('PATCH'), + ); + } + + /** + * @dataProvider requestMethodProvider + */ + public function testSubmitRequest($method) + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $values = array('author' => $this->values); + $files = array('author' => $this->filesNested); + $request = new Request(array(), $values, array(), array(), $files, array( + 'REQUEST_METHOD' => $method, + )); + + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $config = new FormConfigBuilder('author', null, $dispatcher); + $form = new Form($config); + $event = new FormEvent($form, $request); + + $listener = new BindRequestListener(); + DeprecationErrorHandler::preBind($listener, $event); + + $this->assertEquals(array( + 'name' => 'Bernhard', + 'image' => $this->uploadedFile, + ), $event->getData()); + } + + /** + * @dataProvider requestMethodProvider + */ + public function testSubmitRequestWithEmptyName($method) + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $request = new Request(array(), $this->values, array(), array(), $this->filesPlain, array( + 'REQUEST_METHOD' => $method, + )); + + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $config = new FormConfigBuilder('', null, $dispatcher); + $form = new Form($config); + $event = new FormEvent($form, $request); + + $listener = new BindRequestListener(); + DeprecationErrorHandler::preBind($listener, $event); + + $this->assertEquals(array( + 'name' => 'Bernhard', + 'image' => $this->uploadedFile, + ), $event->getData()); + } + + /** + * @dataProvider requestMethodProvider + */ + public function testSubmitEmptyRequestToCompoundForm($method) + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $request = new Request(array(), array(), array(), array(), array(), array( + 'REQUEST_METHOD' => $method, + )); + + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $config = new FormConfigBuilder('author', null, $dispatcher); + $config->setCompound(true); + $config->setDataMapper($this->getMock('Symfony\Component\Form\DataMapperInterface')); + $form = new Form($config); + $event = new FormEvent($form, $request); + + $listener = new BindRequestListener(); + DeprecationErrorHandler::preBind($listener, $event); + + // Default to empty array + $this->assertEquals(array(), $event->getData()); + } + + /** + * @dataProvider requestMethodProvider + */ + public function testSubmitEmptyRequestToSimpleForm($method) + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $request = new Request(array(), array(), array(), array(), array(), array( + 'REQUEST_METHOD' => $method, + )); + + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $config = new FormConfigBuilder('author', null, $dispatcher); + $config->setCompound(false); + $form = new Form($config); + $event = new FormEvent($form, $request); + + $listener = new BindRequestListener(); + DeprecationErrorHandler::preBind($listener, $event); + + // Default to null + $this->assertNull($event->getData()); + } + + public function testSubmitGetRequest() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $values = array('author' => $this->values); + $request = new Request($values, array(), array(), array(), array(), array( + 'REQUEST_METHOD' => 'GET', + )); + + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $config = new FormConfigBuilder('author', null, $dispatcher); + $form = new Form($config); + $event = new FormEvent($form, $request); + + $listener = new BindRequestListener(); + DeprecationErrorHandler::preBind($listener, $event); + + $this->assertEquals(array( + 'name' => 'Bernhard', + 'image' => array('filename' => 'foobar.png'), + ), $event->getData()); + } + + public function testSubmitGetRequestWithEmptyName() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $request = new Request($this->values, array(), array(), array(), array(), array( + 'REQUEST_METHOD' => 'GET', + )); + + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $config = new FormConfigBuilder('', null, $dispatcher); + $form = new Form($config); + $event = new FormEvent($form, $request); + + $listener = new BindRequestListener(); + DeprecationErrorHandler::preBind($listener, $event); + + $this->assertEquals(array( + 'name' => 'Bernhard', + 'image' => array('filename' => 'foobar.png'), + ), $event->getData()); + } + + public function testSubmitEmptyGetRequestToCompoundForm() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $request = new Request(array(), array(), array(), array(), array(), array( + 'REQUEST_METHOD' => 'GET', + )); + + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $config = new FormConfigBuilder('author', null, $dispatcher); + $config->setCompound(true); + $config->setDataMapper($this->getMock('Symfony\Component\Form\DataMapperInterface')); + $form = new Form($config); + $event = new FormEvent($form, $request); + + $listener = new BindRequestListener(); + DeprecationErrorHandler::preBind($listener, $event); + + $this->assertEquals(array(), $event->getData()); + } + + public function testSubmitEmptyGetRequestToSimpleForm() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $request = new Request(array(), array(), array(), array(), array(), array( + 'REQUEST_METHOD' => 'GET', + )); + + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $config = new FormConfigBuilder('author', null, $dispatcher); + $config->setCompound(false); + $form = new Form($config); + $event = new FormEvent($form, $request); + + $listener = new BindRequestListener(); + DeprecationErrorHandler::preBind($listener, $event); + + $this->assertNull($event->getData()); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php new file mode 100644 index 00000000..2d5cf776 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\HttpFoundation; + +use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationRequestHandler; +use Symfony\Component\Form\Tests\AbstractRequestHandlerTest; +use Symfony\Component\HttpFoundation\Request; + +/** + * @author Bernhard Schussek + */ +class HttpFoundationRequestHandlerTest extends AbstractRequestHandlerTest +{ + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + */ + public function testRequestShouldNotBeNull() + { + $this->requestHandler->handleRequest($this->getMockForm('name', 'GET')); + } + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + */ + public function testRequestShouldBeInstanceOfRequest() + { + $this->requestHandler->handleRequest($this->getMockForm('name', 'GET'), new \stdClass()); + } + + protected function setRequestData($method, $data, $files = array()) + { + $this->request = Request::create('http://localhost', $method, $data, array(), $files); + } + + protected function getRequestHandler() + { + return new HttpFoundationRequestHandler(); + } + + protected function getMockFile() + { + return $this->getMockBuilder('Symfony\Component\HttpFoundation\File\UploadedFile') + ->disableOriginalConstructor() + ->getMock(); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php new file mode 100644 index 00000000..a8bdde8a --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php @@ -0,0 +1,748 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Validator\Constraints; + +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\CallbackTransformer; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\Extension\Validator\Constraints\Form; +use Symfony\Component\Form\Extension\Validator\Constraints\FormValidator; +use Symfony\Component\Form\SubmitButtonBuilder; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Constraints\NotNull; +use Symfony\Component\Validator\Constraints\NotBlank; + +/** + * @author Bernhard Schussek + */ +class FormValidatorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $dispatcher; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $factory; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $serverParams; + + /** + * @var FormValidator + */ + private $validator; + + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\Event')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); + $this->serverParams = $this->getMock( + 'Symfony\Component\Form\Extension\Validator\Util\ServerParams', + array('getNormalizedIniPostMaxSize', 'getContentLength') + ); + $this->validator = new FormValidator($this->serverParams); + } + + public function testValidate() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + $options = array('validation_groups' => array('group1', 'group2')); + $form = $this->getBuilder('name', '\stdClass', $options) + ->setData($object) + ->getForm(); + + $context->expects($this->at(0)) + ->method('validate') + ->with($object, 'data', 'group1', true); + $context->expects($this->at(1)) + ->method('validate') + ->with($object, 'data', 'group2', true); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testValidateConstraints() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + $constraint1 = new NotNull(array('groups' => array('group1', 'group2'))); + $constraint2 = new NotBlank(array('groups' => 'group2')); + + $options = array( + 'validation_groups' => array('group1', 'group2'), + 'constraints' => array($constraint1, $constraint2), + ); + $form = $this->getBuilder('name', '\stdClass', $options) + ->setData($object) + ->getForm(); + + // First default constraints + $context->expects($this->at(0)) + ->method('validate') + ->with($object, 'data', 'group1', true); + $context->expects($this->at(1)) + ->method('validate') + ->with($object, 'data', 'group2', true); + + // Then custom constraints + $context->expects($this->at(2)) + ->method('validateValue') + ->with($object, $constraint1, 'data', 'group1'); + $context->expects($this->at(3)) + ->method('validateValue') + ->with($object, $constraint2, 'data', 'group2'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testDontValidateIfParentWithoutCascadeValidation() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + + $parent = $this->getBuilder('parent', null, array('cascade_validation' => false)) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $options = array('validation_groups' => array('group1', 'group2')); + $form = $this->getBuilder('name', '\stdClass', $options)->getForm(); + $parent->add($form); + + $form->setData($object); + + $context->expects($this->never()) + ->method('validate'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testValidateConstraintsEvenIfNoCascadeValidation() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + $constraint1 = new NotNull(array('groups' => array('group1', 'group2'))); + $constraint2 = new NotBlank(array('groups' => 'group2')); + + $parent = $this->getBuilder('parent', null, array('cascade_validation' => false)) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $options = array( + 'validation_groups' => array('group1', 'group2'), + 'constraints' => array($constraint1, $constraint2), + ); + $form = $this->getBuilder('name', '\stdClass', $options) + ->setData($object) + ->getForm(); + $parent->add($form); + + $context->expects($this->at(0)) + ->method('validateValue') + ->with($object, $constraint1, 'data', 'group1'); + $context->expects($this->at(1)) + ->method('validateValue') + ->with($object, $constraint2, 'data', 'group2'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testDontValidateIfNoValidationGroups() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + + $form = $this->getBuilder('name', '\stdClass', array( + 'validation_groups' => array(), + )) + ->setData($object) + ->getForm(); + + $form->setData($object); + + $context->expects($this->never()) + ->method('validate'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testDontValidateConstraintsIfNoValidationGroups() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + $constraint1 = $this->getMock('Symfony\Component\Validator\Constraint'); + $constraint2 = $this->getMock('Symfony\Component\Validator\Constraint'); + + $options = array( + 'validation_groups' => array(), + 'constraints' => array($constraint1, $constraint2), + ); + $form = $this->getBuilder('name', '\stdClass', $options) + ->setData($object) + ->getForm(); + + // Launch transformer + $form->submit(array()); + + $context->expects($this->never()) + ->method('validate'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testDontValidateIfNotSynchronized() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + + $form = $this->getBuilder('name', '\stdClass', array( + 'invalid_message' => 'invalid_message_key', + // Invalid message parameters must be supported, because the + // invalid message can be a translation key + // see https://github.com/symfony/symfony/issues/5144 + 'invalid_message_parameters' => array('{{ foo }}' => 'bar'), + )) + ->setData($object) + ->addViewTransformer(new CallbackTransformer( + function ($data) { return $data; }, + function () { throw new TransformationFailedException(); } + )) + ->getForm(); + + // Launch transformer + $form->submit('foo'); + + $context->expects($this->never()) + ->method('validate'); + + $context->expects($this->once()) + ->method('addViolation') + ->with( + 'invalid_message_key', + array('{{ value }}' => 'foo', '{{ foo }}' => 'bar'), + 'foo' + ); + $context->expects($this->never()) + ->method('addViolationAt'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testAddInvalidErrorEvenIfNoValidationGroups() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + + $form = $this->getBuilder('name', '\stdClass', array( + 'invalid_message' => 'invalid_message_key', + // Invalid message parameters must be supported, because the + // invalid message can be a translation key + // see https://github.com/symfony/symfony/issues/5144 + 'invalid_message_parameters' => array('{{ foo }}' => 'bar'), + 'validation_groups' => array(), + )) + ->setData($object) + ->addViewTransformer(new CallbackTransformer( + function ($data) { return $data; }, + function () { throw new TransformationFailedException(); } + )) + ->getForm(); + + // Launch transformer + $form->submit('foo'); + + $context->expects($this->never()) + ->method('validate'); + + $context->expects($this->once()) + ->method('addViolation') + ->with( + 'invalid_message_key', + array('{{ value }}' => 'foo', '{{ foo }}' => 'bar'), + 'foo' + ); + $context->expects($this->never()) + ->method('addViolationAt'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testDontValidateConstraintsIfNotSynchronized() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + $constraint1 = $this->getMock('Symfony\Component\Validator\Constraint'); + $constraint2 = $this->getMock('Symfony\Component\Validator\Constraint'); + + $options = array( + 'validation_groups' => array('group1', 'group2'), + 'constraints' => array($constraint1, $constraint2), + ); + $form = $this->getBuilder('name', '\stdClass', $options) + ->setData($object) + ->addViewTransformer(new CallbackTransformer( + function ($data) { return $data; }, + function () { throw new TransformationFailedException(); } + )) + ->getForm(); + + // Launch transformer + $form->submit(array()); + + $context->expects($this->never()) + ->method('validate'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + // https://github.com/symfony/symfony/issues/4359 + public function testDontMarkInvalidIfAnyChildIsNotSynchronized() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + + $failingTransformer = new CallbackTransformer( + function ($data) { return $data; }, + function () { throw new TransformationFailedException(); } + ); + + $form = $this->getBuilder('name', '\stdClass') + ->setData($object) + ->addViewTransformer($failingTransformer) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->add( + $this->getBuilder('child') + ->addViewTransformer($failingTransformer) + ) + ->getForm(); + + // Launch transformer + $form->submit(array('child' => 'foo')); + + $context->expects($this->never()) + ->method('addViolation'); + $context->expects($this->never()) + ->method('addViolationAt'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testHandleCallbackValidationGroups() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + $options = array('validation_groups' => array($this, 'getValidationGroups')); + $form = $this->getBuilder('name', '\stdClass', $options) + ->setData($object) + ->getForm(); + + $context->expects($this->at(0)) + ->method('validate') + ->with($object, 'data', 'group1', true); + $context->expects($this->at(1)) + ->method('validate') + ->with($object, 'data', 'group2', true); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testDontExecuteFunctionNames() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + $options = array('validation_groups' => 'header'); + $form = $this->getBuilder('name', '\stdClass', $options) + ->setData($object) + ->getForm(); + + $context->expects($this->once()) + ->method('validate') + ->with($object, 'data', 'header', true); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testHandleClosureValidationGroups() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + $options = array('validation_groups' => function(FormInterface $form){ + return array('group1', 'group2'); + }); + $form = $this->getBuilder('name', '\stdClass', $options) + ->setData($object) + ->getForm(); + + $context->expects($this->at(0)) + ->method('validate') + ->with($object, 'data', 'group1', true); + $context->expects($this->at(1)) + ->method('validate') + ->with($object, 'data', 'group2', true); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testUseValidationGroupOfClickedButton() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + + $parent = $this->getBuilder('parent', null, array('cascade_validation' => true)) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $form = $this->getForm('name', '\stdClass', array( + 'validation_groups' => 'form_group', + )); + + $parent->add($form); + $parent->add($this->getClickedSubmitButton('submit', array( + 'validation_groups' => 'button_group', + ))); + + $form->setData($object); + + $context->expects($this->once()) + ->method('validate') + ->with($object, 'data', 'button_group', true); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testDontUseValidationGroupOfUnclickedButton() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + + $parent = $this->getBuilder('parent', null, array('cascade_validation' => true)) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $form = $this->getForm('name', '\stdClass', array( + 'validation_groups' => 'form_group', + )); + + $parent->add($form); + $parent->add($this->getSubmitButton('submit', array( + 'validation_groups' => 'button_group', + ))); + + $form->setData($object); + + $context->expects($this->once()) + ->method('validate') + ->with($object, 'data', 'form_group', true); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testUseInheritedValidationGroup() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + + $parentOptions = array( + 'validation_groups' => 'group', + 'cascade_validation' => true, + ); + $parent = $this->getBuilder('parent', null, $parentOptions) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $form = $this->getBuilder('name', '\stdClass')->getForm(); + $parent->add($form); + + $form->setData($object); + + $context->expects($this->once()) + ->method('validate') + ->with($object, 'data', 'group', true); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testUseInheritedCallbackValidationGroup() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + + $parentOptions = array( + 'validation_groups' => array($this, 'getValidationGroups'), + 'cascade_validation' => true, + ); + $parent = $this->getBuilder('parent', null, $parentOptions) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $form = $this->getBuilder('name', '\stdClass')->getForm(); + $parent->add($form); + + $form->setData($object); + + $context->expects($this->at(0)) + ->method('validate') + ->with($object, 'data', 'group1', true); + $context->expects($this->at(1)) + ->method('validate') + ->with($object, 'data', 'group2', true); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testUseInheritedClosureValidationGroup() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + + $parentOptions = array( + 'validation_groups' => function(FormInterface $form){ + return array('group1', 'group2'); + }, + 'cascade_validation' => true, + ); + $parent = $this->getBuilder('parent', null, $parentOptions) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $form = $this->getBuilder('name', '\stdClass')->getForm(); + $parent->add($form); + + $form->setData($object); + + $context->expects($this->at(0)) + ->method('validate') + ->with($object, 'data', 'group1', true); + $context->expects($this->at(1)) + ->method('validate') + ->with($object, 'data', 'group2', true); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testAppendPropertyPath() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + $form = $this->getBuilder('name', '\stdClass') + ->setData($object) + ->getForm(); + + $context->expects($this->once()) + ->method('validate') + ->with($object, 'data', 'Default', true); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testDontWalkScalars() + { + $context = $this->getMockExecutionContext(); + + $form = $this->getBuilder() + ->setData('scalar') + ->getForm(); + + $context->expects($this->never()) + ->method('validate'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testViolationIfExtraData() + { + $context = $this->getMockExecutionContext(); + + $form = $this->getBuilder('parent', null, array('extra_fields_message' => 'Extra!')) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->add($this->getBuilder('child')) + ->getForm(); + + $form->submit(array('foo' => 'bar')); + + $context->expects($this->once()) + ->method('addViolation') + ->with( + 'Extra!', + array('{{ extra_fields }}' => 'foo'), + array('foo' => 'bar') + ); + $context->expects($this->never()) + ->method('addViolationAt'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + /** + * @dataProvider getPostMaxSizeFixtures + */ + public function testPostMaxSizeViolation($contentLength, $iniMax, $nbViolation, array $params = array()) + { + $this->serverParams->expects($this->once()) + ->method('getContentLength') + ->will($this->returnValue($contentLength)); + $this->serverParams->expects($this->any()) + ->method('getNormalizedIniPostMaxSize') + ->will($this->returnValue($iniMax)); + + $context = $this->getMockExecutionContext(); + $options = array('post_max_size_message' => 'Max {{ max }}!'); + $form = $this->getBuilder('name', null, $options)->getForm(); + + for ($i = 0; $i < $nbViolation; ++$i) { + if (0 === $i && count($params) > 0) { + $context->expects($this->at($i)) + ->method('addViolation') + ->with($options['post_max_size_message'], $params); + } else { + $context->expects($this->at($i)) + ->method('addViolation'); + } + } + + $context->expects($this->never()) + ->method('addViolationAt'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function getPostMaxSizeFixtures() + { + return array( + array(pow(1024, 3) + 1, '1G', 1, array('{{ max }}' => '1G')), + array(pow(1024, 3), '1G', 0), + array(pow(1024, 2) + 1, '1M', 1, array('{{ max }}' => '1M')), + array(pow(1024, 2), '1M', 0), + array(1024 + 1, '1K', 1, array('{{ max }}' => '1K')), + array(1024, '1K', 0), + array(null, '1K', 0), + array(1024, '', 0), + array(1024, 0, 0), + ); + } + + public function testNoViolationIfNotRoot() + { + $this->serverParams->expects($this->once()) + ->method('getContentLength') + ->will($this->returnValue(1025)); + $this->serverParams->expects($this->never()) + ->method('getNormalizedIniPostMaxSize'); + + $context = $this->getMockExecutionContext(); + $parent = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $form = $this->getForm(); + $parent->add($form); + + $context->expects($this->never()) + ->method('addViolation'); + $context->expects($this->never()) + ->method('addViolationAt'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + /** + * Access has to be public, as this method is called via callback array + * in {@link testValidateFormDataCanHandleCallbackValidationGroups()} + * and {@link testValidateFormDataUsesInheritedCallbackValidationGroup()} + */ + public function getValidationGroups(FormInterface $form) + { + return array('group1', 'group2'); + } + + private function getMockExecutionContext() + { + return $this->getMock('Symfony\Component\Validator\ExecutionContextInterface'); + } + + /** + * @param string $name + * @param string $dataClass + * @param array $options + * + * @return FormBuilder + */ + private function getBuilder($name = 'name', $dataClass = null, array $options = array()) + { + $options = array_replace(array( + 'constraints' => array(), + 'invalid_message_parameters' => array(), + ), $options); + + return new FormBuilder($name, $dataClass, $this->dispatcher, $this->factory, $options); + } + + private function getForm($name = 'name', $dataClass = null, array $options = array()) + { + return $this->getBuilder($name, $dataClass, $options)->getForm(); + } + + private function getSubmitButton($name = 'name', array $options = array()) + { + $builder = new SubmitButtonBuilder($name, $options); + + return $builder->getForm(); + } + + private function getClickedSubmitButton($name = 'name', array $options = array()) + { + return $this->getSubmitButton($name, $options)->submit(''); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getDataMapper() + { + return $this->getMock('Symfony\Component\Form\DataMapperInterface'); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/EventListener/ValidationListenerTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/EventListener/ValidationListenerTest.php new file mode 100644 index 00000000..528f9463 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/EventListener/ValidationListenerTest.php @@ -0,0 +1,145 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Validator\EventListener; + +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Extension\Validator\Constraints\Form; +use Symfony\Component\Form\Extension\Validator\EventListener\ValidationListener; +use Symfony\Component\PropertyAccess\PropertyPath; +use Symfony\Component\Validator\ConstraintViolation; + +class ValidationListenerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $dispatcher; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $factory; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $validator; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $violationMapper; + + /** + * @var ValidationListener + */ + private $listener; + + private $message; + + private $messageTemplate; + + private $params; + + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\Event')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); + $this->validator = $this->getMock('Symfony\Component\Validator\ValidatorInterface'); + $this->violationMapper = $this->getMock('Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapperInterface'); + $this->listener = new ValidationListener($this->validator, $this->violationMapper); + $this->message = 'Message'; + $this->messageTemplate = 'Message template'; + $this->params = array('foo' => 'bar'); + } + + private function getConstraintViolation($code = null) + { + return new ConstraintViolation($this->message, $this->messageTemplate, $this->params, null, 'prop.path', null, null, $code); + } + + private function getBuilder($name = 'name', $propertyPath = null, $dataClass = null) + { + $builder = new FormBuilder($name, $dataClass, $this->dispatcher, $this->factory); + $builder->setPropertyPath(new PropertyPath($propertyPath ?: $name)); + $builder->setAttribute('error_mapping', array()); + $builder->setErrorBubbling(false); + $builder->setMapped(true); + + return $builder; + } + + private function getForm($name = 'name', $propertyPath = null, $dataClass = null) + { + return $this->getBuilder($name, $propertyPath, $dataClass)->getForm(); + } + + private function getMockForm() + { + return $this->getMock('Symfony\Component\Form\Test\FormInterface'); + } + + // More specific mapping tests can be found in ViolationMapperTest + public function testMapViolation() + { + $violation = $this->getConstraintViolation(); + $form = $this->getForm('street'); + + $this->validator->expects($this->once()) + ->method('validate') + ->will($this->returnValue(array($violation))); + + $this->violationMapper->expects($this->once()) + ->method('mapViolation') + ->with($violation, $form, false); + + $this->listener->validateForm(new FormEvent($form, null)); + } + + public function testMapViolationAllowsNonSyncIfInvalid() + { + $violation = $this->getConstraintViolation(Form::ERR_INVALID); + $form = $this->getForm('street'); + + $this->validator->expects($this->once()) + ->method('validate') + ->will($this->returnValue(array($violation))); + + $this->violationMapper->expects($this->once()) + ->method('mapViolation') + // pass true now + ->with($violation, $form, true); + + $this->listener->validateForm(new FormEvent($form, null)); + } + + public function testValidateIgnoresNonRoot() + { + $form = $this->getMockForm(); + $form->expects($this->once()) + ->method('isRoot') + ->will($this->returnValue(false)); + + $this->validator->expects($this->never()) + ->method('validate'); + + $this->violationMapper->expects($this->never()) + ->method('mapViolation'); + + $this->listener->validateForm(new FormEvent($form, null)); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php new file mode 100644 index 00000000..66194105 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Validator\Type; + +use Symfony\Component\Form\FormInterface; + +class FormTypeValidatorExtensionTest extends TypeTestCase +{ + public function testValidationGroupNullByDefault() + { + $form = $this->factory->create('form'); + + $this->assertNull($form->getConfig()->getOption('validation_groups')); + } + + public function testValidationGroupsTransformedToArray() + { + $form = $this->factory->create('form', null, array( + 'validation_groups' => 'group', + )); + + $this->assertEquals(array('group'), $form->getConfig()->getOption('validation_groups')); + } + + public function testValidationGroupsCanBeSetToArray() + { + $form = $this->factory->create('form', null, array( + 'validation_groups' => array('group1', 'group2'), + )); + + $this->assertEquals(array('group1', 'group2'), $form->getConfig()->getOption('validation_groups')); + } + + public function testValidationGroupsCanBeSetToFalse() + { + $form = $this->factory->create('form', null, array( + 'validation_groups' => false, + )); + + $this->assertEquals(array(), $form->getConfig()->getOption('validation_groups')); + } + + public function testValidationGroupsCanBeSetToCallback() + { + $form = $this->factory->create('form', null, array( + 'validation_groups' => array($this, 'testValidationGroupsCanBeSetToCallback'), + )); + + $this->assertTrue(is_callable($form->getConfig()->getOption('validation_groups'))); + } + + public function testValidationGroupsCanBeSetToClosure() + { + $form = $this->factory->create('form', null, array( + 'validation_groups' => function(FormInterface $form){ return null; }, + )); + + $this->assertTrue(is_callable($form->getConfig()->getOption('validation_groups'))); + } + + public function testSubmitValidatesData() + { + $builder = $this->factory->createBuilder('form', null, array( + 'validation_groups' => 'group', + )); + $builder->add('firstName', 'form'); + $form = $builder->getForm(); + + $this->validator->expects($this->once()) + ->method('validate') + ->with($this->equalTo($form)); + + // specific data is irrelevant + $form->submit(array()); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/Type/TypeTestCase.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/Type/TypeTestCase.php new file mode 100644 index 00000000..d94d896a --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/Type/TypeTestCase.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Validator\Type; + +use Symfony\Component\Form\Test\TypeTestCase as BaseTypeTestCase; +use Symfony\Component\Form\Extension\Validator\ValidatorExtension; + +abstract class TypeTestCase extends BaseTypeTestCase +{ + protected $validator; + + protected function setUp() + { + if (!class_exists('Symfony\Component\Validator\Constraint')) { + $this->markTestSkipped('The "Validator" component is not available'); + } + + $this->validator = $this->getMock('Symfony\Component\Validator\ValidatorInterface'); + $metadataFactory = $this->getMock('Symfony\Component\Validator\MetadataFactoryInterface'); + $this->validator->expects($this->once())->method('getMetadataFactory')->will($this->returnValue($metadataFactory)); + $metadata = $this->getMockBuilder('Symfony\Component\Validator\Mapping\ClassMetadata')->disableOriginalConstructor()->getMock(); + $metadataFactory->expects($this->once())->method('getMetadataFor')->will($this->returnValue($metadata)); + + parent::setUp(); + } + + protected function tearDown() + { + $this->validator = null; + + parent::tearDown(); + } + + protected function getExtensions() + { + return array_merge(parent::getExtensions(), array( + new ValidatorExtension($this->validator), + )); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/Util/ServerParamsTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/Util/ServerParamsTest.php new file mode 100644 index 00000000..7ad5b771 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/Util/ServerParamsTest.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Validator\Util; + +class ServerParamsTest extends \PHPUnit_Framework_TestCase +{ + /** @dataProvider getGetPostMaxSizeTestData */ + public function testGetPostMaxSize($size, $bytes) + { + $serverParams = $this->getMock('Symfony\Component\Form\Extension\Validator\Util\ServerParams', array('getNormalizedIniPostMaxSize')); + $serverParams + ->expects($this->any()) + ->method('getNormalizedIniPostMaxSize') + ->will($this->returnValue(strtoupper($size))); + + $this->assertEquals($bytes, $serverParams->getPostMaxSize()); + } + + public function getGetPostMaxSizeTestData() + { + return array( + array('2k', 2048), + array('2 k', 2048), + array('8m', 8 * 1024 * 1024), + array('+2 k', 2048), + array('+2???k', 2048), + array('0x10', 16), + array('0xf', 15), + array('010', 8), + array('+0x10 k', 16 * 1024), + array('1g', 1024 * 1024 * 1024), + array('-1', -1), + array('0', 0), + array('2mk', 2048), // the unit must be the last char, so in this case 'k', not 'm' + ); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php new file mode 100644 index 00000000..c802ea7e --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php @@ -0,0 +1,1481 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Validator\ViolationMapper; + +use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapper; +use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\CallbackTransformer; +use Symfony\Component\Form\Form; +use Symfony\Component\Form\FormConfigBuilder; +use Symfony\Component\Form\FormError; +use Symfony\Component\PropertyAccess\PropertyPath; +use Symfony\Component\Validator\ConstraintViolation; + +/** + * @author Bernhard Schussek + */ +class ViolationMapperTest extends \PHPUnit_Framework_TestCase +{ + const LEVEL_0 = 0; + + const LEVEL_1 = 1; + + const LEVEL_1B = 2; + + const LEVEL_2 = 3; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $dispatcher; + + /** + * @var ViolationMapper + */ + private $mapper; + + /** + * @var string + */ + private $message; + + /** + * @var string + */ + private $messageTemplate; + + /** + * @var array + */ + private $params; + + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\Event')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->mapper = new ViolationMapper(); + $this->message = 'Message'; + $this->messageTemplate = 'Message template'; + $this->params = array('foo' => 'bar'); + } + + protected function getForm($name = 'name', $propertyPath = null, $dataClass = null, $errorMapping = array(), $inheritData = false, $synchronized = true) + { + $config = new FormConfigBuilder($name, $dataClass, $this->dispatcher, array( + 'error_mapping' => $errorMapping, + )); + $config->setMapped(true); + $config->setInheritData($inheritData); + $config->setPropertyPath($propertyPath); + $config->setCompound(true); + $config->setDataMapper($this->getDataMapper()); + + if (!$synchronized) { + $config->addViewTransformer(new CallbackTransformer( + function ($normData) { return $normData; }, + function () { throw new TransformationFailedException(); } + )); + } + + return new Form($config); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getDataMapper() + { + return $this->getMock('Symfony\Component\Form\DataMapperInterface'); + } + + /** + * @param $propertyPath + * + * @return ConstraintViolation + */ + protected function getConstraintViolation($propertyPath) + { + return new ConstraintViolation($this->message, $this->messageTemplate, $this->params, null, $propertyPath, null); + } + + /** + * @return FormError + */ + protected function getFormError() + { + return new FormError($this->message, $this->messageTemplate, $this->params); + } + + public function testMapToFormInheritingParentDataIfDataDoesNotMatch() + { + $violation = $this->getConstraintViolation('children[address].data.foo'); + $parent = $this->getForm('parent'); + $child = $this->getForm('address', 'address', null, array(), true); + $grandChild = $this->getForm('street'); + + $parent->add($child); + $child->add($grandChild); + + $this->mapper->mapViolation($violation, $parent); + + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertEquals(array($this->getFormError()), $child->getErrors(), $child->getName().' should have an error, but has none'); + $this->assertCount(0, $grandChild->getErrors(), $grandChild->getName().' should not have an error, but has one'); + } + + public function testFollowDotRules() + { + $violation = $this->getConstraintViolation('data.foo'); + $parent = $this->getForm('parent', null, null, array( + 'foo' => 'address', + )); + $child = $this->getForm('address', null, null, array( + '.' => 'street', + )); + $grandChild = $this->getForm('street', null, null, array( + '.' => 'name', + )); + $grandGrandChild = $this->getForm('name'); + + $parent->add($child); + $child->add($grandChild); + $grandChild->add($grandGrandChild); + + $this->mapper->mapViolation($violation, $parent); + + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertCount(0, $child->getErrors(), $child->getName().' should not have an error, but has one'); + $this->assertCount(0, $grandChild->getErrors(), $grandChild->getName().' should not have an error, but has one'); + $this->assertEquals(array($this->getFormError()), $grandGrandChild->getErrors(), $grandGrandChild->getName().' should have an error, but has none'); + } + + public function testAbortMappingIfNotSynchronized() + { + $violation = $this->getConstraintViolation('children[address].data.street'); + $parent = $this->getForm('parent'); + $child = $this->getForm('address', 'address', null, array(), false, false); + // even though "street" is synchronized, it should not have any errors + // due to its parent not being synchronized + $grandChild = $this->getForm('street' , 'street'); + + $parent->add($child); + $child->add($grandChild); + + // submit to invoke the transformer and mark the form unsynchronized + $parent->submit(array()); + + $this->mapper->mapViolation($violation, $parent); + + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertCount(0, $child->getErrors(), $child->getName().' should not have an error, but has one'); + $this->assertCount(0, $grandChild->getErrors(), $grandChild->getName().' should not have an error, but has one'); + } + + public function testAbortDotRuleMappingIfNotSynchronized() + { + $violation = $this->getConstraintViolation('data.address'); + $parent = $this->getForm('parent'); + $child = $this->getForm('address', 'address', null, array( + '.' => 'street', + ), false, false); + // even though "street" is synchronized, it should not have any errors + // due to its parent not being synchronized + $grandChild = $this->getForm('street'); + + $parent->add($child); + $child->add($grandChild); + + // submit to invoke the transformer and mark the form unsynchronized + $parent->submit(array()); + + $this->mapper->mapViolation($violation, $parent); + + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertCount(0, $child->getErrors(), $child->getName().' should not have an error, but has one'); + $this->assertCount(0, $grandChild->getErrors(), $grandChild->getName().' should not have an error, but has one'); + } + + public function provideDefaultTests() + { + // The mapping must be deterministic! If a child has the property path "[street]", + // "data[street]" should be mapped, but "data.street" should not! + return array( + // mapping target, child name, its property path, grand child name, its property path, violation path + array(self::LEVEL_0, 'address', 'address', 'street', 'street', ''), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data'), + + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'street', 'children[address].data'), + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'children[address].data.street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'street', 'children[address].data[street].prop'), + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'data.address.street'), + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'data.address.street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'street', 'data.address[street]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'street', 'data.address[street].prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data[address].street'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data[address].street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data[address][street]'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data[address][street].prop'), + + array(self::LEVEL_2, 'address', 'address', 'street', '[street]', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', 'address', 'street', '[street]', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[street]', 'children[address].data'), + array(self::LEVEL_1, 'address', 'address', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_1, 'address', 'address', 'street', '[street]', 'children[address].data.street.prop'), + array(self::LEVEL_2, 'address', 'address', 'street', '[street]', 'children[address].data[street]'), + array(self::LEVEL_2, 'address', 'address', 'street', '[street]', 'children[address].data[street].prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[street]', 'data.address.street'), + array(self::LEVEL_1, 'address', 'address', 'street', '[street]', 'data.address.street.prop'), + array(self::LEVEL_2, 'address', 'address', 'street', '[street]', 'data.address[street]'), + array(self::LEVEL_2, 'address', 'address', 'street', '[street]', 'data.address[street].prop'), + array(self::LEVEL_0, 'address', 'address', 'street', '[street]', 'data[address].street'), + array(self::LEVEL_0, 'address', 'address', 'street', '[street]', 'data[address].street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', '[street]', 'data[address][street]'), + array(self::LEVEL_0, 'address', 'address', 'street', '[street]', 'data[address][street].prop'), + + array(self::LEVEL_2, 'address', '[address]', 'street', 'street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'street', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'street', 'children[address].data'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'street', 'children[address].data.street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'street', 'children[address].data[street].prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'street', 'data.address.street'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'street', 'data.address.street.prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'street', 'data.address[street]'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'street', 'data.address[street].prop'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'street', 'data[address].street'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'street', 'data[address].street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'street', 'data[address][street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'street', 'data[address][street].prop'), + + array(self::LEVEL_2, 'address', '[address]', 'street', '[street]', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[street]', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[street]', 'children[address].data'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[street]', 'children[address].data.street.prop'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[street]', 'children[address].data[street]'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[street]', 'children[address].data[street].prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[street]', 'data.address.street'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[street]', 'data.address.street.prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[street]', 'data.address[street]'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[street]', 'data.address[street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[street]', 'data[address].street'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[street]', 'data[address].street.prop'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[street]', 'data[address][street]'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[street]', 'data[address][street].prop'), + + array(self::LEVEL_2, 'address', 'person.address', 'street', 'street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', 'person.address', 'street', 'street', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', 'person.address', 'street', 'street', 'children[address].data'), + array(self::LEVEL_2, 'address', 'person.address', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_2, 'address', 'person.address', 'street', 'street', 'children[address].data.street.prop'), + array(self::LEVEL_1, 'address', 'person.address', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_1, 'address', 'person.address', 'street', 'street', 'children[address].data[street].prop'), + array(self::LEVEL_2, 'address', 'person.address', 'street', 'street', 'data.person.address.street'), + array(self::LEVEL_2, 'address', 'person.address', 'street', 'street', 'data.person.address.street.prop'), + array(self::LEVEL_1, 'address', 'person.address', 'street', 'street', 'data.person.address[street]'), + array(self::LEVEL_1, 'address', 'person.address', 'street', 'street', 'data.person.address[street].prop'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data.person[address].street'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data.person[address].street.prop'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data.person[address][street]'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data.person[address][street].prop'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data[person].address.street'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data[person].address.street.prop'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data[person].address[street]'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data[person].address[street].prop'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data[person][address].street'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data[person][address].street.prop'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data[person][address][street]'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data[person][address][street].prop'), + + array(self::LEVEL_2, 'address', 'person.address', 'street', '[street]', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', 'person.address', 'street', '[street]', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', 'person.address', 'street', '[street]', 'children[address].data'), + array(self::LEVEL_1, 'address', 'person.address', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_1, 'address', 'person.address', 'street', '[street]', 'children[address].data.street.prop'), + array(self::LEVEL_2, 'address', 'person.address', 'street', '[street]', 'children[address].data[street]'), + array(self::LEVEL_2, 'address', 'person.address', 'street', '[street]', 'children[address].data[street].prop'), + array(self::LEVEL_1, 'address', 'person.address', 'street', '[street]', 'data.person.address.street'), + array(self::LEVEL_1, 'address', 'person.address', 'street', '[street]', 'data.person.address.street.prop'), + array(self::LEVEL_2, 'address', 'person.address', 'street', '[street]', 'data.person.address[street]'), + array(self::LEVEL_2, 'address', 'person.address', 'street', '[street]', 'data.person.address[street].prop'), + array(self::LEVEL_0, 'address', 'person.address', 'street', '[street]', 'data.person[address].street'), + array(self::LEVEL_0, 'address', 'person.address', 'street', '[street]', 'data.person[address].street.prop'), + array(self::LEVEL_0, 'address', 'person.address', 'street', '[street]', 'data.person[address][street]'), + array(self::LEVEL_0, 'address', 'person.address', 'street', '[street]', 'data.person[address][street].prop'), + array(self::LEVEL_0, 'address', 'person.address', 'street', '[street]', 'data[person].address.street'), + array(self::LEVEL_0, 'address', 'person.address', 'street', '[street]', 'data[person].address.street.prop'), + array(self::LEVEL_0, 'address', 'person.address', 'street', '[street]', 'data[person].address[street]'), + array(self::LEVEL_0, 'address', 'person.address', 'street', '[street]', 'data[person].address[street].prop'), + array(self::LEVEL_0, 'address', 'person.address', 'street', '[street]', 'data[person][address].street'), + array(self::LEVEL_0, 'address', 'person.address', 'street', '[street]', 'data[person][address].street.prop'), + array(self::LEVEL_0, 'address', 'person.address', 'street', '[street]', 'data[person][address][street]'), + array(self::LEVEL_0, 'address', 'person.address', 'street', '[street]', 'data[person][address][street].prop'), + + array(self::LEVEL_2, 'address', 'person[address]', 'street', 'street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', 'person[address]', 'street', 'street', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', 'person[address]', 'street', 'street', 'children[address].data'), + array(self::LEVEL_2, 'address', 'person[address]', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_2, 'address', 'person[address]', 'street', 'street', 'children[address].data.street.prop'), + array(self::LEVEL_1, 'address', 'person[address]', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_1, 'address', 'person[address]', 'street', 'street', 'children[address].data[street].prop'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', 'street', 'data.person.address.street'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', 'street', 'data.person.address.street.prop'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', 'street', 'data.person.address[street]'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', 'street', 'data.person.address[street].prop'), + array(self::LEVEL_2, 'address', 'person[address]', 'street', 'street', 'data.person[address].street'), + array(self::LEVEL_2, 'address', 'person[address]', 'street', 'street', 'data.person[address].street.prop'), + array(self::LEVEL_1, 'address', 'person[address]', 'street', 'street', 'data.person[address][street]'), + array(self::LEVEL_1, 'address', 'person[address]', 'street', 'street', 'data.person[address][street].prop'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', 'street', 'data[person].address.street'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', 'street', 'data[person].address.street.prop'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', 'street', 'data[person].address[street]'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', 'street', 'data[person].address[street].prop'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', 'street', 'data[person][address].street'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', 'street', 'data[person][address].street.prop'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', 'street', 'data[person][address][street]'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', 'street', 'data[person][address][street].prop'), + + array(self::LEVEL_2, 'address', 'person[address]', 'street', '[street]', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', 'person[address]', 'street', '[street]', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', 'person[address]', 'street', '[street]', 'children[address].data'), + array(self::LEVEL_1, 'address', 'person[address]', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_1, 'address', 'person[address]', 'street', '[street]', 'children[address].data.street.prop'), + array(self::LEVEL_2, 'address', 'person[address]', 'street', '[street]', 'children[address].data[street]'), + array(self::LEVEL_2, 'address', 'person[address]', 'street', '[street]', 'children[address].data[street].prop'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', '[street]', 'data.person.address.street'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', '[street]', 'data.person.address.street.prop'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', '[street]', 'data.person.address[street]'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', '[street]', 'data.person.address[street].prop'), + array(self::LEVEL_1, 'address', 'person[address]', 'street', '[street]', 'data.person[address].street'), + array(self::LEVEL_1, 'address', 'person[address]', 'street', '[street]', 'data.person[address].street.prop'), + array(self::LEVEL_2, 'address', 'person[address]', 'street', '[street]', 'data.person[address][street]'), + array(self::LEVEL_2, 'address', 'person[address]', 'street', '[street]', 'data.person[address][street].prop'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', '[street]', 'data[person].address.street'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', '[street]', 'data[person].address.street.prop'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', '[street]', 'data[person].address[street]'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', '[street]', 'data[person].address[street].prop'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', '[street]', 'data[person][address].street'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', '[street]', 'data[person][address].street.prop'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', '[street]', 'data[person][address][street]'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', '[street]', 'data[person][address][street].prop'), + + array(self::LEVEL_2, 'address', '[person].address', 'street', 'street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', '[person].address', 'street', 'street', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', '[person].address', 'street', 'street', 'children[address].data'), + array(self::LEVEL_2, 'address', '[person].address', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_2, 'address', '[person].address', 'street', 'street', 'children[address].data.street.prop'), + array(self::LEVEL_1, 'address', '[person].address', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_1, 'address', '[person].address', 'street', 'street', 'children[address].data[street].prop'), + array(self::LEVEL_0, 'address', '[person].address', 'street', 'street', 'data.person.address.street'), + array(self::LEVEL_0, 'address', '[person].address', 'street', 'street', 'data.person.address.street.prop'), + array(self::LEVEL_0, 'address', '[person].address', 'street', 'street', 'data.person.address[street]'), + array(self::LEVEL_0, 'address', '[person].address', 'street', 'street', 'data.person.address[street].prop'), + array(self::LEVEL_0, 'address', '[person].address', 'street', 'street', 'data.person[address].street'), + array(self::LEVEL_0, 'address', '[person].address', 'street', 'street', 'data.person[address].street.prop'), + array(self::LEVEL_0, 'address', '[person].address', 'street', 'street', 'data.person[address][street]'), + array(self::LEVEL_0, 'address', '[person].address', 'street', 'street', 'data.person[address][street].prop'), + array(self::LEVEL_2, 'address', '[person].address', 'street', 'street', 'data[person].address.street'), + array(self::LEVEL_2, 'address', '[person].address', 'street', 'street', 'data[person].address.street.prop'), + array(self::LEVEL_1, 'address', '[person].address', 'street', 'street', 'data[person].address[street]'), + array(self::LEVEL_1, 'address', '[person].address', 'street', 'street', 'data[person].address[street].prop'), + array(self::LEVEL_0, 'address', '[person].address', 'street', 'street', 'data[person][address].street'), + array(self::LEVEL_0, 'address', '[person].address', 'street', 'street', 'data[person][address].street.prop'), + array(self::LEVEL_0, 'address', '[person].address', 'street', 'street', 'data[person][address][street]'), + array(self::LEVEL_0, 'address', '[person].address', 'street', 'street', 'data[person][address][street].prop'), + + array(self::LEVEL_2, 'address', '[person].address', 'street', '[street]', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', '[person].address', 'street', '[street]', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', '[person].address', 'street', '[street]', 'children[address].data'), + array(self::LEVEL_1, 'address', '[person].address', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_1, 'address', '[person].address', 'street', '[street]', 'children[address].data.street.prop'), + array(self::LEVEL_2, 'address', '[person].address', 'street', '[street]', 'children[address].data[street]'), + array(self::LEVEL_2, 'address', '[person].address', 'street', '[street]', 'children[address].data[street].prop'), + array(self::LEVEL_0, 'address', '[person].address', 'street', '[street]', 'data.person.address.street'), + array(self::LEVEL_0, 'address', '[person].address', 'street', '[street]', 'data.person.address.street.prop'), + array(self::LEVEL_0, 'address', '[person].address', 'street', '[street]', 'data.person.address[street]'), + array(self::LEVEL_0, 'address', '[person].address', 'street', '[street]', 'data.person.address[street].prop'), + array(self::LEVEL_0, 'address', '[person].address', 'street', '[street]', 'data.person[address].street'), + array(self::LEVEL_0, 'address', '[person].address', 'street', '[street]', 'data.person[address].street.prop'), + array(self::LEVEL_0, 'address', '[person].address', 'street', '[street]', 'data.person[address][street]'), + array(self::LEVEL_0, 'address', '[person].address', 'street', '[street]', 'data.person[address][street].prop'), + array(self::LEVEL_1, 'address', '[person].address', 'street', '[street]', 'data[person].address.street'), + array(self::LEVEL_1, 'address', '[person].address', 'street', '[street]', 'data[person].address.street.prop'), + array(self::LEVEL_2, 'address', '[person].address', 'street', '[street]', 'data[person].address[street]'), + array(self::LEVEL_2, 'address', '[person].address', 'street', '[street]', 'data[person].address[street].prop'), + array(self::LEVEL_0, 'address', '[person].address', 'street', '[street]', 'data[person][address].street'), + array(self::LEVEL_0, 'address', '[person].address', 'street', '[street]', 'data[person][address].street.prop'), + array(self::LEVEL_0, 'address', '[person].address', 'street', '[street]', 'data[person][address][street]'), + array(self::LEVEL_0, 'address', '[person].address', 'street', '[street]', 'data[person][address][street].prop'), + + array(self::LEVEL_2, 'address', '[person][address]', 'street', 'street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', '[person][address]', 'street', 'street', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', '[person][address]', 'street', 'street', 'children[address]'), + array(self::LEVEL_1, 'address', '[person][address]', 'street', 'street', 'children[address].data'), + array(self::LEVEL_2, 'address', '[person][address]', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_2, 'address', '[person][address]', 'street', 'street', 'children[address].data.street.prop'), + array(self::LEVEL_1, 'address', '[person][address]', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_1, 'address', '[person][address]', 'street', 'street', 'children[address].data[street].prop'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', 'street', 'data.person.address.street'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', 'street', 'data.person.address.street.prop'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', 'street', 'data.person.address[street]'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', 'street', 'data.person.address[street].prop'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', 'street', 'data.person[address].street'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', 'street', 'data.person[address].street.prop'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', 'street', 'data.person[address][street]'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', 'street', 'data.person[address][street].prop'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', 'street', 'data[person].address.street'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', 'street', 'data[person].address.street.prop'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', 'street', 'data[person].address[street]'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', 'street', 'data[person].address[street].prop'), + array(self::LEVEL_2, 'address', '[person][address]', 'street', 'street', 'data[person][address].street'), + array(self::LEVEL_2, 'address', '[person][address]', 'street', 'street', 'data[person][address].street.prop'), + array(self::LEVEL_1, 'address', '[person][address]', 'street', 'street', 'data[person][address][street]'), + array(self::LEVEL_1, 'address', '[person][address]', 'street', 'street', 'data[person][address][street].prop'), + + array(self::LEVEL_2, 'address', '[person][address]', 'street', '[street]', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', '[person][address]', 'street', '[street]', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', '[person][address]', 'street', '[street]', 'children[address].data'), + array(self::LEVEL_1, 'address', '[person][address]', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_1, 'address', '[person][address]', 'street', '[street]', 'children[address].data.street.prop'), + array(self::LEVEL_2, 'address', '[person][address]', 'street', '[street]', 'children[address].data[street]'), + array(self::LEVEL_2, 'address', '[person][address]', 'street', '[street]', 'children[address].data[street].prop'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', '[street]', 'data.person.address.street'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', '[street]', 'data.person.address.street.prop'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', '[street]', 'data.person.address[street]'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', '[street]', 'data.person.address[street].prop'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', '[street]', 'data.person[address].street'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', '[street]', 'data.person[address].street.prop'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', '[street]', 'data.person[address][street]'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', '[street]', 'data.person[address][street].prop'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', '[street]', 'data[person].address.street'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', '[street]', 'data[person].address.street.prop'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', '[street]', 'data[person].address[street]'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', '[street]', 'data[person].address[street].prop'), + array(self::LEVEL_1, 'address', '[person][address]', 'street', '[street]', 'data[person][address].street'), + array(self::LEVEL_1, 'address', '[person][address]', 'street', '[street]', 'data[person][address].street.prop'), + array(self::LEVEL_2, 'address', '[person][address]', 'street', '[street]', 'data[person][address][street]'), + array(self::LEVEL_2, 'address', '[person][address]', 'street', '[street]', 'data[person][address][street].prop'), + + array(self::LEVEL_2, 'address', 'address', 'street', 'office.street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', 'address', 'street', 'office.street', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'children[address].data'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'children[address].data.office'), + array(self::LEVEL_2, 'address', 'address', 'street', 'office.street', 'children[address].data.office.street'), + array(self::LEVEL_2, 'address', 'address', 'street', 'office.street', 'children[address].data.office.street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'children[address].data.office[street]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'children[address].data.office[street].prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'children[address].data[office]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'children[address].data[office].street'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'children[address].data[office].street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'children[address].data[office][street]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'children[address].data[office][street].prop'), + array(self::LEVEL_2, 'address', 'address', 'street', 'office.street', 'data.address.office.street'), + array(self::LEVEL_2, 'address', 'address', 'street', 'office.street', 'data.address.office.street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'data.address.office[street]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'data.address.office[street].prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'data.address[office].street'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'data.address[office].street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'data.address[office][street]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'data.address[office][street].prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office.street', 'data[address].office.street'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office.street', 'data[address].office.street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office.street', 'data[address].office[street]'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office.street', 'data[address].office[street].prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office.street', 'data[address][office].street'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office.street', 'data[address][office].street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office.street', 'data[address][office][street]'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office.street', 'data[address][office][street].prop'), + + array(self::LEVEL_2, 'address', '[address]', 'street', 'office.street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'office.street', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'children[address].data'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'children[address].data.office'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'office.street', 'children[address].data.office.street'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'office.street', 'children[address].data.office.street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'children[address].data.office[street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'children[address].data.office[street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'children[address].data[office]'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'children[address].data[office].street'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'children[address].data[office].street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'children[address].data[office][street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'children[address].data[office][street].prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office.street', 'data.address.office.street'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office.street', 'data.address.office.street.prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office.street', 'data.address.office[street]'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office.street', 'data.address.office[street].prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office.street', 'data.address[office].street'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office.street', 'data.address[office].street.prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office.street', 'data.address[office][street]'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office.street', 'data.address[office][street].prop'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'office.street', 'data[address].office.street'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'office.street', 'data[address].office.street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'data[address].office[street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'data[address].office[street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'data[address][office].street'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'data[address][office].street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'data[address][office][street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'data[address][office][street].prop'), + + array(self::LEVEL_2, 'address', 'address', 'street', 'office[street]', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', 'address', 'street', 'office[street]', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'children[address].data'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'children[address].data.office'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'children[address].data.office.street'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'children[address].data.office.street.prop'), + array(self::LEVEL_2, 'address', 'address', 'street', 'office[street]', 'children[address].data.office[street]'), + array(self::LEVEL_2, 'address', 'address', 'street', 'office[street]', 'children[address].data.office[street].prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'children[address].data[office]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'children[address].data[office].street'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'children[address].data[office].street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'children[address].data[office][street]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'children[address].data[office][street].prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'data.address.office.street'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'data.address.office.street.prop'), + array(self::LEVEL_2, 'address', 'address', 'street', 'office[street]', 'data.address.office[street]'), + array(self::LEVEL_2, 'address', 'address', 'street', 'office[street]', 'data.address.office[street].prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'data.address[office].street'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'data.address[office].street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'data.address[office][street]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'data.address[office][street].prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office[street]', 'data[address].office.street'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office[street]', 'data[address].office.street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office[street]', 'data[address].office[street]'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office[street]', 'data[address].office[street].prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office[street]', 'data[address][office].street'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office[street]', 'data[address][office].street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office[street]', 'data[address][office][street]'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office[street]', 'data[address][office][street].prop'), + + array(self::LEVEL_2, 'address', '[address]', 'street', 'office[street]', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'office[street]', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'children[address].data.office.street'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'children[address].data.office.street.prop'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'office[street]', 'children[address].data.office[street]'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'office[street]', 'children[address].data.office[street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'children[address].data[office]'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'children[address].data[office].street'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'children[address].data[office].street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'children[address].data[office][street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'children[address].data[office][street].prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office[street]', 'data.address.office.street'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office[street]', 'data.address.office.street.prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office[street]', 'data.address.office[street]'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office[street]', 'data.address.office[street].prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office[street]', 'data.address[office].street'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office[street]', 'data.address[office].street.prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office[street]', 'data.address[office][street]'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office[street]', 'data.address[office][street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'data[address].office.street'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'data[address].office.street.prop'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'office[street]', 'data[address].office[street]'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'office[street]', 'data[address].office[street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'data[address][office].street'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'data[address][office].street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'data[address][office][street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'data[address][office][street].prop'), + + array(self::LEVEL_2, 'address', 'address', 'street', '[office].street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', 'address', 'street', '[office].street', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'children[address].data'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'children[address].data.office'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'children[address].data.office.street'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'children[address].data.office.street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'children[address].data.office[street]'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'children[address].data.office[street].prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'children[address].data[office]'), + array(self::LEVEL_2, 'address', 'address', 'street', '[office].street', 'children[address].data[office].street'), + array(self::LEVEL_2, 'address', 'address', 'street', '[office].street', 'children[address].data[office].street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'children[address].data[office][street]'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'children[address].data[office][street].prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'data.address.office.street'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'data.address.office.street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'data.address.office[street]'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'data.address.office[street].prop'), + array(self::LEVEL_2, 'address', 'address', 'street', '[office].street', 'data.address[office].street'), + array(self::LEVEL_2, 'address', 'address', 'street', '[office].street', 'data.address[office].street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'data.address[office][street]'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'data.address[office][street].prop'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office].street', 'data[address].office.street'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office].street', 'data[address].office.street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office].street', 'data[address].office[street]'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office].street', 'data[address].office[street].prop'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office].street', 'data[address][office].street'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office].street', 'data[address][office].street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office].street', 'data[address][office][street]'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office].street', 'data[address][office][street].prop'), + + array(self::LEVEL_2, 'address', '[address]', 'street', '[office].street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[office].street', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'children[address].data'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'children[address].data.office'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'children[address].data.office.street'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'children[address].data.office.street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'children[address].data.office[street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'children[address].data.office[street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'children[address].data[office]'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[office].street', 'children[address].data[office].street'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[office].street', 'children[address].data[office].street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'children[address].data[office][street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'children[address].data[office][street].prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office].street', 'data.address.office.street'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office].street', 'data.address.office.street.prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office].street', 'data.address.office[street]'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office].street', 'data.address.office[street].prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office].street', 'data.address[office].street'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office].street', 'data.address[office].street.prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office].street', 'data.address[office][street]'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office].street', 'data.address[office][street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'data[address].office.street'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'data[address].office.street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'data[address].office[street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'data[address].office[street].prop'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[office].street', 'data[address][office].street'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[office].street', 'data[address][office].street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'data[address][office][street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'data[address][office][street].prop'), + + array(self::LEVEL_2, 'address', 'address', 'street', '[office][street]', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', 'address', 'street', '[office][street]', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'children[address].data'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'children[address].data.office'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'children[address].data.office.street'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'children[address].data.office.street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'children[address].data.office[street]'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'children[address].data.office[street].prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'children[address].data[office]'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'children[address].data[office].street'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'children[address].data[office].street.prop'), + array(self::LEVEL_2, 'address', 'address', 'street', '[office][street]', 'children[address].data[office][street]'), + array(self::LEVEL_2, 'address', 'address', 'street', '[office][street]', 'children[address].data[office][street].prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'data.address.office.street'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'data.address.office.street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'data.address.office[street]'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'data.address.office[street].prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'data.address[office].street'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'data.address[office].street.prop'), + array(self::LEVEL_2, 'address', 'address', 'street', '[office][street]', 'data.address[office][street]'), + array(self::LEVEL_2, 'address', 'address', 'street', '[office][street]', 'data.address[office][street].prop'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office][street]', 'data[address].office.street'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office][street]', 'data[address].office.street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office][street]', 'data[address].office[street]'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office][street]', 'data[address].office[street].prop'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office][street]', 'data[address][office].street'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office][street]', 'data[address][office].street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office][street]', 'data[address][office][street]'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office][street]', 'data[address][office][street].prop'), + + array(self::LEVEL_2, 'address', '[address]', 'street', '[office][street]', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[office][street]', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'children[address].data'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'children[address].data.office'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'children[address].data.office.street'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'children[address].data.office.street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'children[address].data.office[street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'children[address].data.office[street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'children[address].data[office]'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'children[address].data[office].street'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'children[address].data[office].street.prop'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[office][street]', 'children[address].data[office][street]'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[office][street]', 'children[address].data[office][street].prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office][street]', 'data.address.office.street'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office][street]', 'data.address.office.street.prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office][street]', 'data.address.office[street]'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office][street]', 'data.address.office[street].prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office][street]', 'data.address[office].street'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office][street]', 'data.address[office].street.prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office][street]', 'data.address[office][street]'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office][street]', 'data.address[office][street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'data[address].office.street'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'data[address].office.street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'data[address].office[street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'data[address].office[street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'data[address][office].street'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'data[address][office].street.prop'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[office][street]', 'data[address][office][street]'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[office][street]', 'data[address][office][street].prop'), + + // Edge cases which must not occur + array(self::LEVEL_1, 'address', 'address', 'street', 'street', 'children[address][street]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'street', 'children[address][street].prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[street]', 'children[address][street]'), + array(self::LEVEL_1, 'address', 'address', 'street', '[street]', 'children[address][street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'street', 'children[address][street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'street', 'children[address][street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[street]', 'children[address][street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[street]', 'children[address][street].prop'), + + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'children[person].children[address].children[street]'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'children[person].children[address].data.street'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'children[person].data.address.street'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data.address.street'), + + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'children[address].children[office].children[street]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'children[address].children[office].data.street'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'children[address].data.street'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'data.address.street'), + ); + } + + /** + * @dataProvider provideDefaultTests + */ + public function testDefaultErrorMapping($target, $childName, $childPath, $grandChildName, $grandChildPath, $violationPath) + { + $violation = $this->getConstraintViolation($violationPath); + $parent = $this->getForm('parent'); + $child = $this->getForm($childName, $childPath); + $grandChild = $this->getForm($grandChildName, $grandChildPath); + + $parent->add($child); + $child->add($grandChild); + + $this->mapper->mapViolation($violation, $parent); + + if (self::LEVEL_0 === $target) { + $this->assertEquals(array($this->getFormError()), $parent->getErrors(), $parent->getName().' should have an error, but has none'); + $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); + $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); + } elseif (self::LEVEL_1 === $target) { + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertEquals(array($this->getFormError()), $child->getErrors(), $childName.' should have an error, but has none'); + $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); + } else { + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); + $this->assertEquals(array($this->getFormError()), $grandChild->getErrors(), $grandChildName.' should have an error, but has none'); + } + } + + public function provideCustomDataErrorTests() + { + return array( + // mapping target, error mapping, child name, its property path, grand child name, its property path, violation path + array(self::LEVEL_1, 'foo', 'address', 'address', 'address', 'street', 'street', 'data.foo'), + array(self::LEVEL_1, 'foo', 'address', 'address', 'address', 'street', 'street', 'data.foo.prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', 'street', 'data[foo]'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', 'street', 'data[foo].prop'), + + array(self::LEVEL_1, 'foo', 'address', 'address', 'address', 'street', 'street', 'data.address'), + array(self::LEVEL_1, 'foo', 'address', 'address', 'address', 'street', 'street', 'data.address.prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', 'street', 'data[address]'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', 'street', 'data[address].prop'), + + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.foo'), + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.foo.prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[foo]'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[foo].prop'), + + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.address'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.address.prop'), + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[address]'), + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[address].prop'), + + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.foo'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.foo.prop'), + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[foo]'), + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[foo].prop'), + + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.address'), + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.address.prop'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[address]'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[address].prop'), + + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data.foo'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data.foo.prop'), + array(self::LEVEL_1, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data[foo]'), + array(self::LEVEL_1, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data[foo].prop'), + + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data.address'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data.address.prop'), + array(self::LEVEL_1, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data[address]'), + array(self::LEVEL_1, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data[address].prop'), + + array(self::LEVEL_2, 'foo', 'address', 'address', 'address', 'street', 'street', 'data.foo.street'), + array(self::LEVEL_2, 'foo', 'address', 'address', 'address', 'street', 'street', 'data.foo.street.prop'), + array(self::LEVEL_1, 'foo', 'address', 'address', 'address', 'street', 'street', 'data.foo[street]'), + array(self::LEVEL_1, 'foo', 'address', 'address', 'address', 'street', 'street', 'data.foo[street].prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', 'street', 'data[foo].street'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', 'street', 'data[foo].street.prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', 'street', 'data[foo][street]'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', 'street', 'data[foo][street].prop'), + + array(self::LEVEL_2, 'foo', 'address', 'address', 'address', 'street', 'street', 'data.address.street'), + array(self::LEVEL_2, 'foo', 'address', 'address', 'address', 'street', 'street', 'data.address.street.prop'), + array(self::LEVEL_1, 'foo', 'address', 'address', 'address', 'street', 'street', 'data.address[street]'), + array(self::LEVEL_1, 'foo', 'address', 'address', 'address', 'street', 'street', 'data.address[street].prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', 'street', 'data[address].street'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', 'street', 'data[address].street.prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', 'street', 'data[address][street]'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', 'street', 'data[address][street].prop'), + + array(self::LEVEL_1, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data.foo.street'), + array(self::LEVEL_1, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data.foo.street.prop'), + array(self::LEVEL_2, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data.foo[street]'), + array(self::LEVEL_2, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data.foo[street].prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data[foo].street'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data[foo].street.prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data[foo][street]'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data[foo][street].prop'), + + array(self::LEVEL_1, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data.address.street'), + array(self::LEVEL_1, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data.address.street.prop'), + array(self::LEVEL_2, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data.address[street]'), + array(self::LEVEL_2, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data.address[street].prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data[address].street'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data[address].street.prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data[address][street]'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data[address][street].prop'), + + array(self::LEVEL_2, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.foo.street'), + array(self::LEVEL_2, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.foo.street.prop'), + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.foo[street]'), + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.foo[street].prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[foo].street'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[foo].street.prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[foo][street]'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[foo][street].prop'), + + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.address.street'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.address.street.prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.address[street]'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.address[street].prop'), + array(self::LEVEL_2, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[address].street'), + array(self::LEVEL_2, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[address].street.prop'), + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[address][street]'), + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[address][street].prop'), + + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data.foo.street'), + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data.foo.street.prop'), + array(self::LEVEL_2, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data.foo[street]'), + array(self::LEVEL_2, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data.foo[street].prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data[foo].street'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data[foo].street.prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data[foo][street]'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data[foo][street].prop'), + + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data.address.street'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data.address.street.prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data.address[street]'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data.address[street].prop'), + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data[address].street'), + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data[address].street.prop'), + array(self::LEVEL_2, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data[address][street]'), + array(self::LEVEL_2, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data[address][street].prop'), + + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.foo.street'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.foo.street.prop'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.foo[street]'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.foo[street].prop'), + array(self::LEVEL_2, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[foo].street'), + array(self::LEVEL_2, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[foo].street.prop'), + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[foo][street]'), + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[foo][street].prop'), + + array(self::LEVEL_2, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.address.street'), + array(self::LEVEL_2, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.address.street.prop'), + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.address[street]'), + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.address[street].prop'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[address].street'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[address].street.prop'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[address][street]'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[address][street].prop'), + + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data.foo.street'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data.foo.street.prop'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data.foo[street]'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data.foo[street].prop'), + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data[foo].street'), + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data[foo].street.prop'), + array(self::LEVEL_2, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data[foo][street]'), + array(self::LEVEL_2, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data[foo][street].prop'), + + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data.address.street'), + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data.address.street.prop'), + array(self::LEVEL_2, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data.address[street]'), + array(self::LEVEL_2, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data.address[street].prop'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data[address].street'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data[address].street.prop'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data[address][street]'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data[address][street].prop'), + + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data.foo.street'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data.foo.street.prop'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data.foo[street]'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data.foo[street].prop'), + array(self::LEVEL_2, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data[foo].street'), + array(self::LEVEL_2, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data[foo].street.prop'), + array(self::LEVEL_1, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data[foo][street]'), + array(self::LEVEL_1, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data[foo][street].prop'), + + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data.address.street'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data.address.street.prop'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data.address[street]'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data.address[street].prop'), + array(self::LEVEL_2, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data[address].street'), + array(self::LEVEL_2, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data[address].street.prop'), + array(self::LEVEL_1, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data[address][street]'), + array(self::LEVEL_1, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data[address][street].prop'), + + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data.foo.street'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data.foo.street.prop'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data.foo[street]'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data.foo[street].prop'), + array(self::LEVEL_1, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data[foo].street'), + array(self::LEVEL_1, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data[foo].street.prop'), + array(self::LEVEL_2, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data[foo][street]'), + array(self::LEVEL_2, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data[foo][street].prop'), + + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data.address.street'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data.address.street.prop'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data.address[street]'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data.address[street].prop'), + array(self::LEVEL_1, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data[address].street'), + array(self::LEVEL_1, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data[address].street.prop'), + array(self::LEVEL_2, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data[address][street]'), + array(self::LEVEL_2, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data[address][street].prop'), + + array(self::LEVEL_1, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar'), + array(self::LEVEL_1, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar.prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar]'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar].prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar.prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar]'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar].prop'), + + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar.prop'), + array(self::LEVEL_1, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar]'), + array(self::LEVEL_1, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar].prop'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar.prop'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar]'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar].prop'), + + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar.prop'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar]'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar].prop'), + array(self::LEVEL_1, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar'), + array(self::LEVEL_1, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar.prop'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar]'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar].prop'), + + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar.prop'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar]'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar].prop'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar.prop'), + array(self::LEVEL_1, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar]'), + array(self::LEVEL_1, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar].prop'), + + array(self::LEVEL_2, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar.street'), + array(self::LEVEL_2, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar.street.prop'), + array(self::LEVEL_1, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar[street]'), + array(self::LEVEL_1, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar[street].prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar].street'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar].street.prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar][street]'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar][street].prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar.street'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar.street.prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar[street]'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar[street].prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar].street'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar].street.prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar][street]'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar][street].prop'), + + array(self::LEVEL_1, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar.street'), + array(self::LEVEL_1, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar.street.prop'), + array(self::LEVEL_2, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar[street]'), + array(self::LEVEL_2, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar[street].prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar].street'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar].street.prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar][street]'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar][street].prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar.street'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar.street.prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar[street]'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar[street].prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar].street'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar].street.prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar][street]'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar][street].prop'), + + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar.street'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar.street.prop'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar[street]'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar[street].prop'), + array(self::LEVEL_2, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar].street'), + array(self::LEVEL_2, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar].street.prop'), + array(self::LEVEL_1, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar][street]'), + array(self::LEVEL_1, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar][street].prop'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar.street'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar.street.prop'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar[street]'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar[street].prop'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar].street'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar].street.prop'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar][street]'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar][street].prop'), + + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar.street'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar.street.prop'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar[street]'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar[street].prop'), + array(self::LEVEL_1, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar].street'), + array(self::LEVEL_1, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar].street.prop'), + array(self::LEVEL_2, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar][street]'), + array(self::LEVEL_2, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar][street].prop'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar.street'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar.street.prop'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar[street]'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar[street].prop'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar].street'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar].street.prop'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar][street]'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar][street].prop'), + + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar.street'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar.street.prop'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar[street]'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar[street].prop'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar].street'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar].street.prop'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar][street]'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar][street].prop'), + array(self::LEVEL_2, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar.street'), + array(self::LEVEL_2, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar.street.prop'), + array(self::LEVEL_1, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar[street]'), + array(self::LEVEL_1, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar[street].prop'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar].street'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar].street.prop'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar][street]'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar][street].prop'), + + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar.street'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar.street.prop'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar[street]'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar[street].prop'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar].street'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar].street.prop'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar][street]'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar][street].prop'), + array(self::LEVEL_1, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar.street'), + array(self::LEVEL_1, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar.street.prop'), + array(self::LEVEL_2, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar[street]'), + array(self::LEVEL_2, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar[street].prop'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar].street'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar].street.prop'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar][street]'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar][street].prop'), + + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar.street'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar.street.prop'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar[street]'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar[street].prop'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar].street'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar].street.prop'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar][street]'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar][street].prop'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar.street'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar.street.prop'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar[street]'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar[street].prop'), + array(self::LEVEL_2, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar].street'), + array(self::LEVEL_2, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar].street.prop'), + array(self::LEVEL_1, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar][street]'), + array(self::LEVEL_1, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar][street].prop'), + + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar.street'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar.street.prop'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar[street]'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar[street].prop'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar].street'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar].street.prop'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar][street]'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar][street].prop'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar.street'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar.street.prop'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar[street]'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar[street].prop'), + array(self::LEVEL_1, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar].street'), + array(self::LEVEL_1, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar].street.prop'), + array(self::LEVEL_2, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar][street]'), + array(self::LEVEL_2, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar][street].prop'), + + array(self::LEVEL_2, 'foo', 'address.street', 'address', 'address', 'street', 'street', 'data.foo'), + array(self::LEVEL_2, 'foo', 'address.street', 'address', 'address', 'street', 'street', 'data.foo.prop'), + array(self::LEVEL_2, '[foo]', 'address.street', 'address', 'address', 'street', 'street', 'data[foo]'), + array(self::LEVEL_2, '[foo]', 'address.street', 'address', 'address', 'street', 'street', 'data[foo].prop'), + + array(self::LEVEL_2, 'foo', 'address.street', 'address', 'address', 'street', '[street]', 'data.foo'), + array(self::LEVEL_2, 'foo', 'address.street', 'address', 'address', 'street', '[street]', 'data.foo.prop'), + array(self::LEVEL_2, '[foo]', 'address.street', 'address', 'address', 'street', '[street]', 'data[foo]'), + array(self::LEVEL_2, '[foo]', 'address.street', 'address', 'address', 'street', '[street]', 'data[foo].prop'), + + array(self::LEVEL_2, 'foo', 'address.street', 'address', '[address]', 'street', 'street', 'data.foo'), + array(self::LEVEL_2, 'foo', 'address.street', 'address', '[address]', 'street', 'street', 'data.foo.prop'), + array(self::LEVEL_2, '[foo]', 'address.street', 'address', '[address]', 'street', 'street', 'data[foo]'), + array(self::LEVEL_2, '[foo]', 'address.street', 'address', '[address]', 'street', 'street', 'data[foo].prop'), + + array(self::LEVEL_2, 'foo.bar', 'address.street', 'address', 'address', 'street', 'street', 'data.foo.bar'), + array(self::LEVEL_2, 'foo.bar', 'address.street', 'address', 'address', 'street', 'street', 'data.foo.bar.prop'), + array(self::LEVEL_2, 'foo[bar]', 'address.street', 'address', 'address', 'street', 'street', 'data.foo[bar]'), + array(self::LEVEL_2, 'foo[bar]', 'address.street', 'address', 'address', 'street', 'street', 'data.foo[bar].prop'), + array(self::LEVEL_2, '[foo].bar', 'address.street', 'address', 'address', 'street', 'street', 'data[foo].bar'), + array(self::LEVEL_2, '[foo].bar', 'address.street', 'address', 'address', 'street', 'street', 'data[foo].bar.prop'), + array(self::LEVEL_2, '[foo][bar]', 'address.street', 'address', 'address', 'street', 'street', 'data[foo][bar]'), + array(self::LEVEL_2, '[foo][bar]', 'address.street', 'address', 'address', 'street', 'street', 'data[foo][bar].prop'), + + array(self::LEVEL_2, 'foo.bar', 'address.street', 'address', 'address', 'street', '[street]', 'data.foo.bar'), + array(self::LEVEL_2, 'foo.bar', 'address.street', 'address', 'address', 'street', '[street]', 'data.foo.bar.prop'), + array(self::LEVEL_2, 'foo[bar]', 'address.street', 'address', 'address', 'street', '[street]', 'data.foo[bar]'), + array(self::LEVEL_2, 'foo[bar]', 'address.street', 'address', 'address', 'street', '[street]', 'data.foo[bar].prop'), + array(self::LEVEL_2, '[foo].bar', 'address.street', 'address', 'address', 'street', '[street]', 'data[foo].bar'), + array(self::LEVEL_2, '[foo].bar', 'address.street', 'address', 'address', 'street', '[street]', 'data[foo].bar.prop'), + array(self::LEVEL_2, '[foo][bar]', 'address.street', 'address', 'address', 'street', '[street]', 'data[foo][bar]'), + array(self::LEVEL_2, '[foo][bar]', 'address.street', 'address', 'address', 'street', '[street]', 'data[foo][bar].prop'), + + array(self::LEVEL_2, 'foo.bar', 'address.street', 'address', '[address]', 'street', 'street', 'data.foo.bar'), + array(self::LEVEL_2, 'foo.bar', 'address.street', 'address', '[address]', 'street', 'street', 'data.foo.bar.prop'), + array(self::LEVEL_2, 'foo[bar]', 'address.street', 'address', '[address]', 'street', 'street', 'data.foo[bar]'), + array(self::LEVEL_2, 'foo[bar]', 'address.street', 'address', '[address]', 'street', 'street', 'data.foo[bar].prop'), + array(self::LEVEL_2, '[foo].bar', 'address.street', 'address', '[address]', 'street', 'street', 'data[foo].bar'), + array(self::LEVEL_2, '[foo].bar', 'address.street', 'address', '[address]', 'street', 'street', 'data[foo].bar.prop'), + array(self::LEVEL_2, '[foo][bar]', 'address.street', 'address', '[address]', 'street', 'street', 'data[foo][bar]'), + array(self::LEVEL_2, '[foo][bar]', 'address.street', 'address', '[address]', 'street', 'street', 'data[foo][bar].prop'), + + // Edge cases + array(self::LEVEL_2, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.foo.street'), + array(self::LEVEL_2, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.foo.street.prop'), + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.foo[street]'), + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.foo[street].prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[foo].street'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[foo].street.prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[foo][street]'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[foo][street].prop'), + + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.foo.street'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.foo.street.prop'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.foo[street]'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.foo[street].prop'), + array(self::LEVEL_2, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[foo].street'), + array(self::LEVEL_2, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[foo].street.prop'), + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[foo][street]'), + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[foo][street].prop'), + ); + } + + /** + * @dataProvider provideCustomDataErrorTests + */ + public function testCustomDataErrorMapping($target, $mapFrom, $mapTo, $childName, $childPath, $grandChildName, $grandChildPath, $violationPath) + { + $violation = $this->getConstraintViolation($violationPath); + $parent = $this->getForm('parent', null, null, array($mapFrom => $mapTo)); + $child = $this->getForm($childName, $childPath); + $grandChild = $this->getForm($grandChildName, $grandChildPath); + + $parent->add($child); + $child->add($grandChild); + + // Add a field mapped to the first element of $mapFrom + // to try to distract the algorithm + // Only add it if we expect the error to come up on a different + // level than LEVEL_0, because in this case the error would + // (correctly) be mapped to the distraction field + if ($target !== self::LEVEL_0) { + $mapFromPath = new PropertyPath($mapFrom); + $mapFromPrefix = $mapFromPath->isIndex(0) + ? '['.$mapFromPath->getElement(0).']' + : $mapFromPath->getElement(0); + $distraction = $this->getForm('distraction', $mapFromPrefix); + + $parent->add($distraction); + } + + $this->mapper->mapViolation($violation, $parent); + + if ($target !== self::LEVEL_0) { + $this->assertCount(0, $distraction->getErrors(), 'distraction should not have an error, but has one'); + } + + if (self::LEVEL_0 === $target) { + $this->assertEquals(array($this->getFormError()), $parent->getErrors(), $parent->getName().' should have an error, but has none'); + $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); + $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); + } elseif (self::LEVEL_1 === $target) { + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertEquals(array($this->getFormError()), $child->getErrors(), $childName.' should have an error, but has none'); + $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); + } else { + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); + $this->assertEquals(array($this->getFormError()), $grandChild->getErrors(), $grandChildName.' should have an error, but has none'); + } + } + + public function provideCustomFormErrorTests() + { + // This case is different than the data errors, because here the + // left side of the mapping refers to the property path of the actual + // children. In other words, a child error only works if + // 1) the error actually maps to an existing child and + // 2) the property path of that child (relative to the form providing + // the mapping) matches the left side of the mapping + return array( + // mapping target, map from, map to, child name, its property path, grand child name, its property path, violation path + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo].children[street].data'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo].children[street].data.prop'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo].data.street'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo].data.street.prop'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo].data[street]'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo].data[street].prop'), + + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].children[street].data.prop'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].data.street.prop'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].data[street].prop'), + + // Property path of the erroneous field and mapping must match exactly + array(self::LEVEL_1B, 'foo', 'address', 'foo', '[foo]', 'address', 'address', 'street', 'street', 'children[foo].children[street].data'), + array(self::LEVEL_1B, 'foo', 'address', 'foo', '[foo]', 'address', 'address', 'street', 'street', 'children[foo].children[street].data.prop'), + array(self::LEVEL_1B, 'foo', 'address', 'foo', '[foo]', 'address', 'address', 'street', 'street', 'children[foo].data.street'), + array(self::LEVEL_1B, 'foo', 'address', 'foo', '[foo]', 'address', 'address', 'street', 'street', 'children[foo].data.street.prop'), + array(self::LEVEL_1B, 'foo', 'address', 'foo', '[foo]', 'address', 'address', 'street', 'street', 'children[foo].data[street]'), + array(self::LEVEL_1B, 'foo', 'address', 'foo', '[foo]', 'address', 'address', 'street', 'street', 'children[foo].data[street].prop'), + + array(self::LEVEL_1B, '[foo]', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo].children[street].data'), + array(self::LEVEL_1B, '[foo]', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo].children[street].data.prop'), + array(self::LEVEL_1B, '[foo]', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo].data.street'), + array(self::LEVEL_1B, '[foo]', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo].data.street.prop'), + array(self::LEVEL_1B, '[foo]', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo].data[street]'), + array(self::LEVEL_1B, '[foo]', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo].data[street].prop'), + + array(self::LEVEL_1, '[foo]', 'address', 'foo', '[foo]', 'address', 'address', 'street', 'street', 'children[foo].children[street].data'), + array(self::LEVEL_1, '[foo]', 'address', 'foo', '[foo]', 'address', 'address', 'street', 'street', 'children[foo].children[street].data.prop'), + array(self::LEVEL_2, '[foo]', 'address', 'foo', '[foo]', 'address', 'address', 'street', 'street', 'children[foo].data.street'), + array(self::LEVEL_2, '[foo]', 'address', 'foo', '[foo]', 'address', 'address', 'street', 'street', 'children[foo].data.street.prop'), + array(self::LEVEL_1, '[foo]', 'address', 'foo', '[foo]', 'address', 'address', 'street', 'street', 'children[foo].data[street]'), + array(self::LEVEL_1, '[foo]', 'address', 'foo', '[foo]', 'address', 'address', 'street', 'street', 'children[foo].data[street].prop'), + + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[foo].children[street].data'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[foo].children[street].data.prop'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[foo].data.street'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[foo].data.street.prop'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[foo].data[street]'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[foo].data[street].prop'), + + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].children[street].data'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].data.street.prop'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].data[street]'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].data[street].prop'), + + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[foo].children[street].data'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[foo].children[street].data.prop'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[foo].data.street'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[foo].data.street.prop'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[foo].data[street]'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[foo].data[street].prop'), + + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].children[street].data.prop'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].data.street.prop'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].data[street].prop'), + + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[foo].children[street].data'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[foo].children[street].data.prop'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[foo].data.street'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[foo].data.street.prop'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[foo].data[street]'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[foo].data[street].prop'), + + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].children[street].data'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].data.street.prop'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].data[street]'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].data[street].prop'), + + // Map to a nested child + array(self::LEVEL_2, 'foo', 'address.street', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo]'), + array(self::LEVEL_2, 'foo', 'address.street', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[foo]'), + array(self::LEVEL_2, 'foo', 'address.street', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[foo]'), + array(self::LEVEL_2, 'foo', 'address.street', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[foo]'), + + // Map from a nested child + array(self::LEVEL_1B, 'address.street', 'foo', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].children[street]'), + array(self::LEVEL_1B, 'address.street', 'foo', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_1, 'address.street', 'foo', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_2, 'address.street', 'foo', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].children[street]'), + array(self::LEVEL_1B, 'address.street', 'foo', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_2, 'address.street', 'foo', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].data[street]'), + array(self::LEVEL_2, 'address.street', 'foo', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].children[street]'), + array(self::LEVEL_2, 'address.street', 'foo', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_1, 'address.street', 'foo', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_2, 'address.street', 'foo', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].children[street]'), + array(self::LEVEL_1, 'address.street', 'foo', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_2, 'address.street', 'foo', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].data[street]'), + + array(self::LEVEL_2, 'address[street]', 'foo', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].children[street]'), + array(self::LEVEL_2, 'address[street]', 'foo', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_1B, 'address[street]', 'foo', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_1B, 'address[street]', 'foo', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].children[street]'), + array(self::LEVEL_1, 'address[street]', 'foo', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_1B, 'address[street]', 'foo', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].data[street]'), + array(self::LEVEL_2, 'address[street]', 'foo', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].children[street]'), + array(self::LEVEL_2, 'address[street]', 'foo', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_1, 'address[street]', 'foo', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_2, 'address[street]', 'foo', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].children[street]'), + array(self::LEVEL_1, 'address[street]', 'foo', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_2, 'address[street]', 'foo', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].data[street]'), + + array(self::LEVEL_2, '[address].street', 'foo', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].children[street]'), + array(self::LEVEL_2, '[address].street', 'foo', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_1, '[address].street', 'foo', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_2, '[address].street', 'foo', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].children[street]'), + array(self::LEVEL_1, '[address].street', 'foo', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_2, '[address].street', 'foo', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].data[street]'), + array(self::LEVEL_1B, '[address].street', 'foo', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].children[street]'), + array(self::LEVEL_1B, '[address].street', 'foo', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_1, '[address].street', 'foo', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_2, '[address].street', 'foo', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].children[street]'), + array(self::LEVEL_1B, '[address].street', 'foo', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_2, '[address].street', 'foo', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].data[street]'), + + array(self::LEVEL_2, '[address][street]', 'foo', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].children[street]'), + array(self::LEVEL_2, '[address][street]', 'foo', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_1, '[address][street]', 'foo', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_2, '[address][street]', 'foo', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].children[street]'), + array(self::LEVEL_1, '[address][street]', 'foo', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_2, '[address][street]', 'foo', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].data[street]'), + array(self::LEVEL_2, '[address][street]', 'foo', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].children[street]'), + array(self::LEVEL_2, '[address][street]', 'foo', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_1B, '[address][street]', 'foo', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_1B, '[address][street]', 'foo', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].children[street]'), + array(self::LEVEL_1, '[address][street]', 'foo', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_1B, '[address][street]', 'foo', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].data[street]'), + ); + } + + /** + * @dataProvider provideCustomFormErrorTests + */ + public function testCustomFormErrorMapping($target, $mapFrom, $mapTo, $errorName, $errorPath, $childName, $childPath, $grandChildName, $grandChildPath, $violationPath) + { + $violation = $this->getConstraintViolation($violationPath); + $parent = $this->getForm('parent', null, null, array($mapFrom => $mapTo)); + $child = $this->getForm($childName, $childPath); + $grandChild = $this->getForm($grandChildName, $grandChildPath); + $errorChild = $this->getForm($errorName, $errorPath); + + $parent->add($child); + $parent->add($errorChild); + $child->add($grandChild); + + $this->mapper->mapViolation($violation, $parent); + + if (self::LEVEL_0 === $target) { + $this->assertCount(0, $errorChild->getErrors(), $errorName.' should not have an error, but has one'); + $this->assertEquals(array($this->getFormError()), $parent->getErrors(), $parent->getName().' should have an error, but has none'); + $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); + $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); + } elseif (self::LEVEL_1 === $target) { + $this->assertCount(0, $errorChild->getErrors(), $errorName.' should not have an error, but has one'); + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertEquals(array($this->getFormError()), $child->getErrors(), $childName.' should have an error, but has none'); + $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); + } elseif (self::LEVEL_1B === $target) { + $this->assertEquals(array($this->getFormError()), $errorChild->getErrors(), $errorName.' should have an error, but has none'); + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); + $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); + } else { + $this->assertCount(0, $errorChild->getErrors(), $errorName.' should not have an error, but has one'); + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); + $this->assertEquals(array($this->getFormError()), $grandChild->getErrors(), $grandChildName.' should have an error, but has none'); + } + } + + public function provideErrorTestsForFormInheritingParentData() + { + return array( + // mapping target, child name, its property path, grand child name, its property path, violation path + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'children[address].children[street].data.prop'), + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'children[address].data.street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'street', 'children[address].data[street].prop'), + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'data.street'), + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'data.street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data[street]'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data[street].prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data.address.street'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data.address.street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data.address[street]'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data.address[street].prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data[address].street'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data[address].street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data[address][street]'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data[address][street].prop'), + ); + } + + /** + * @dataProvider provideErrorTestsForFormInheritingParentData + */ + public function testErrorMappingForFormInheritingParentData($target, $childName, $childPath, $grandChildName, $grandChildPath, $violationPath) + { + $violation = $this->getConstraintViolation($violationPath); + $parent = $this->getForm('parent'); + $child = $this->getForm($childName, $childPath, null, array(), true); + $grandChild = $this->getForm($grandChildName, $grandChildPath); + + $parent->add($child); + $child->add($grandChild); + + $this->mapper->mapViolation($violation, $parent); + + if (self::LEVEL_0 === $target) { + $this->assertEquals(array($this->getFormError()), $parent->getErrors(), $parent->getName().' should have an error, but has none'); + $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); + $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); + } elseif (self::LEVEL_1 === $target) { + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertEquals(array($this->getFormError()), $child->getErrors(), $childName.' should have an error, but has none'); + $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); + } else { + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); + $this->assertEquals(array($this->getFormError()), $grandChild->getErrors(), $grandChildName.' should have an error, but has none'); + } + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationPathTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationPathTest.php new file mode 100644 index 00000000..02df8f43 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationPathTest.php @@ -0,0 +1,245 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Validator\ViolationMapper; + +use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationPath; + +/** + * @author Bernhard Schussek + */ +class ViolationPathTest extends \PHPUnit_Framework_TestCase +{ + public function providePaths() + { + return array( + array('children[address]', array( + array('address', true, true), + )), + array('children[address].children[street]', array( + array('address', true, true), + array('street', true, true), + )), + array('children[address][street]', array( + array('address', true, true), + ), 'children[address]'), + array('children[address].data', array( + array('address', true, true), + ), 'children[address]'), + array('children[address].data.street', array( + array('address', true, true), + array('street', false, false), + )), + array('children[address].data[street]', array( + array('address', true, true), + array('street', false, true), + )), + array('children[address].children[street].data.name', array( + array('address', true, true), + array('street', true, true), + array('name', false, false), + )), + array('children[address].children[street].data[name]', array( + array('address', true, true), + array('street', true, true), + array('name', false, true), + )), + array('data.address', array( + array('address', false, false), + )), + array('data[address]', array( + array('address', false, true), + )), + array('data.address.street', array( + array('address', false, false), + array('street', false, false), + )), + array('data[address].street', array( + array('address', false, true), + array('street', false, false), + )), + array('data.address[street]', array( + array('address', false, false), + array('street', false, true), + )), + array('data[address][street]', array( + array('address', false, true), + array('street', false, true), + )), + // A few invalid examples + array('data', array(), ''), + array('children', array(), ''), + array('children.address', array(), ''), + array('children.address[street]', array(), ''), + ); + } + + /** + * @dataProvider providePaths + */ + public function testCreatePath($string, $entries, $slicedPath = null) + { + if (null === $slicedPath) { + $slicedPath = $string; + } + + $path = new ViolationPath($string); + + $this->assertSame($slicedPath, $path->__toString()); + $this->assertSame(count($entries), count($path->getElements())); + $this->assertSame(count($entries), $path->getLength()); + + foreach ($entries as $index => $entry) { + $this->assertEquals($entry[0], $path->getElement($index)); + $this->assertSame($entry[1], $path->mapsForm($index)); + $this->assertSame($entry[2], $path->isIndex($index)); + $this->assertSame(!$entry[2], $path->isProperty($index)); + } + } + + public function provideParents() + { + return array( + array('children[address]', null), + array('children[address].children[street]', 'children[address]'), + array('children[address].data.street', 'children[address]'), + array('children[address].data[street]', 'children[address]'), + array('data.address', null), + array('data.address.street', 'data.address'), + array('data.address[street]', 'data.address'), + array('data[address].street', 'data[address]'), + array('data[address][street]', 'data[address]'), + ); + } + + /** + * @dataProvider provideParents + */ + public function testGetParent($violationPath, $parentPath) + { + $path = new ViolationPath($violationPath); + $parent = $parentPath === null ? null : new ViolationPath($parentPath); + + $this->assertEquals($parent, $path->getParent()); + } + + public function testGetElement() + { + $path = new ViolationPath('children[address].data[street].name'); + + $this->assertEquals('street', $path->getElement(1)); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testGetElementDoesNotAcceptInvalidIndices() + { + $path = new ViolationPath('children[address].data[street].name'); + + $path->getElement(3); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testGetElementDoesNotAcceptNegativeIndices() + { + $path = new ViolationPath('children[address].data[street].name'); + + $path->getElement(-1); + } + + public function testIsProperty() + { + $path = new ViolationPath('children[address].data[street].name'); + + $this->assertFalse($path->isProperty(1)); + $this->assertTrue($path->isProperty(2)); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testIsPropertyDoesNotAcceptInvalidIndices() + { + $path = new ViolationPath('children[address].data[street].name'); + + $path->isProperty(3); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testIsPropertyDoesNotAcceptNegativeIndices() + { + $path = new ViolationPath('children[address].data[street].name'); + + $path->isProperty(-1); + } + + public function testIsIndex() + { + $path = new ViolationPath('children[address].data[street].name'); + + $this->assertTrue($path->isIndex(1)); + $this->assertFalse($path->isIndex(2)); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testIsIndexDoesNotAcceptInvalidIndices() + { + $path = new ViolationPath('children[address].data[street].name'); + + $path->isIndex(3); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testIsIndexDoesNotAcceptNegativeIndices() + { + $path = new ViolationPath('children[address].data[street].name'); + + $path->isIndex(-1); + } + + public function testMapsForm() + { + $path = new ViolationPath('children[address].data[street].name'); + + $this->assertTrue($path->mapsForm(0)); + $this->assertFalse($path->mapsForm(1)); + $this->assertFalse($path->mapsForm(2)); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testMapsFormDoesNotAcceptInvalidIndices() + { + $path = new ViolationPath('children[address].data[street].name'); + + $path->mapsForm(3); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testMapsFormDoesNotAcceptNegativeIndices() + { + $path = new ViolationPath('children[address].data[street].name'); + + $path->mapsForm(-1); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/AlternatingRowType.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/AlternatingRowType.php new file mode 100644 index 00000000..ee7d1353 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/AlternatingRowType.php @@ -0,0 +1,27 @@ +getFormFactory(); + + $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($formFactory) { + $form = $event->getForm(); + $type = $form->getName() % 2 === 0 ? 'text' : 'textarea'; + $form->add('title', $type); + }); + } + + public function getName() + { + return 'alternating_row'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/Author.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/Author.php new file mode 100644 index 00000000..11204894 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/Author.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +class Author +{ + public $firstName; + private $lastName; + private $australian; + public $child; + private $readPermissions; + + private $privateProperty; + + public function setLastName($lastName) + { + $this->lastName = $lastName; + } + + public function getLastName() + { + return $this->lastName; + } + + private function getPrivateGetter() + { + return 'foobar'; + } + + public function setAustralian($australian) + { + $this->australian = $australian; + } + + public function isAustralian() + { + return $this->australian; + } + + public function setReadPermissions($bool) + { + $this->readPermissions = $bool; + } + + public function hasReadPermissions() + { + return $this->readPermissions; + } + + private function isPrivateIsser() + { + return true; + } + + public function getPrivateSetter() + { + } + + private function setPrivateSetter($data) + { + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/AuthorType.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/AuthorType.php new file mode 100644 index 00000000..147f6e48 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/AuthorType.php @@ -0,0 +1,30 @@ +add('firstName') + ->add('lastName') + ; + } + + public function getName() + { + return 'author'; + } + + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + )); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/CustomArrayObject.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/CustomArrayObject.php new file mode 100644 index 00000000..950f677f --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/CustomArrayObject.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +/** + * This class is a hand written simplified version of PHP native `ArrayObject` + * class, to show that it behaves differently than the PHP native implementation. + */ +class CustomArrayObject implements \ArrayAccess, \IteratorAggregate, \Countable, \Serializable +{ + private $array; + + public function __construct(array $array = null) + { + $this->array = $array ?: array(); + } + + public function offsetExists($offset) + { + return array_key_exists($offset, $this->array); + } + + public function offsetGet($offset) + { + return $this->array[$offset]; + } + + public function offsetSet($offset, $value) + { + if (null === $offset) { + $this->array[] = $value; + } else { + $this->array[$offset] = $value; + } + } + + public function offsetUnset($offset) + { + unset($this->array[$offset]); + } + + public function getIterator() + { + return new \ArrayIterator($this->array); + } + + public function count() + { + return count($this->array); + } + + public function serialize() + { + return serialize($this->array); + } + + public function unserialize($serialized) + { + $this->array = (array) unserialize((string) $serialized); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FixedDataTransformer.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FixedDataTransformer.php new file mode 100644 index 00000000..a5a31248 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FixedDataTransformer.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\RuntimeException; + +class FixedDataTransformer implements DataTransformerInterface +{ + private $mapping; + + public function __construct(array $mapping) + { + $this->mapping = $mapping; + } + + public function transform($value) + { + if (!array_key_exists($value, $this->mapping)) { + throw new RuntimeException(sprintf('No mapping for value "%s"', $value)); + } + + return $this->mapping[$value]; + } + + public function reverseTransform($value) + { + $result = array_search($value, $this->mapping, true); + + if ($result === false) { + throw new RuntimeException(sprintf('No reverse mapping for value "%s"', $value)); + } + + return $result; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FixedFilterListener.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FixedFilterListener.php new file mode 100644 index 00000000..762a10b8 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FixedFilterListener.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +class FixedFilterListener implements EventSubscriberInterface +{ + private $mapping; + + public function __construct(array $mapping) + { + $this->mapping = array_merge(array( + 'preSubmit' => array(), + 'onSubmit' => array(), + 'preSetData' => array(), + ), $mapping); + } + + public function preSubmit(FormEvent $event) + { + $data = $event->getData(); + + if (isset($this->mapping['preSubmit'][$data])) { + $event->setData($this->mapping['preSubmit'][$data]); + } + } + + public function onSubmit(FormEvent $event) + { + $data = $event->getData(); + + if (isset($this->mapping['onSubmit'][$data])) { + $event->setData($this->mapping['onSubmit'][$data]); + } + } + + public function preSetData(FormEvent $event) + { + $data = $event->getData(); + + if (isset($this->mapping['preSetData'][$data])) { + $event->setData($this->mapping['preSetData'][$data]); + } + } + + public static function getSubscribedEvents() + { + return array( + FormEvents::PRE_SUBMIT => 'preSubmit', + FormEvents::SUBMIT => 'onSubmit', + FormEvents::PRE_SET_DATA => 'preSetData', + ); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooSubType.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooSubType.php new file mode 100644 index 00000000..4f7ba6d4 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooSubType.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormFactoryInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class FooSubType extends AbstractType +{ + public function getName() + { + return 'foo_sub_type'; + } + + public function getParent() + { + return 'foo'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooSubTypeWithParentInstance.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooSubTypeWithParentInstance.php new file mode 100644 index 00000000..468b5a32 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooSubTypeWithParentInstance.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormFactoryInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class FooSubTypeWithParentInstance extends AbstractType +{ + public function getName() + { + return 'foo_sub_type_parent_instance'; + } + + public function getParent() + { + return new FooType(); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooType.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooType.php new file mode 100644 index 00000000..d26d3f76 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooType.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormFactoryInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class FooType extends AbstractType +{ + public function getName() + { + return 'foo'; + } + + public function getParent() + { + return null; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooTypeBarExtension.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooTypeBarExtension.php new file mode 100644 index 00000000..c5f92e11 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooTypeBarExtension.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +use Symfony\Component\Form\AbstractTypeExtension; +use Symfony\Component\Form\FormBuilderInterface; + +class FooTypeBarExtension extends AbstractTypeExtension +{ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->setAttribute('bar', 'x'); + } + + public function getAllowedOptionValues() + { + return array( + 'a_or_b' => array('c'), + ); + } + + public function getExtendedType() + { + return 'foo'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooTypeBazExtension.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooTypeBazExtension.php new file mode 100644 index 00000000..2e364754 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooTypeBazExtension.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +use Symfony\Component\Form\AbstractTypeExtension; +use Symfony\Component\Form\FormBuilderInterface; + +class FooTypeBazExtension extends AbstractTypeExtension +{ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->setAttribute('baz', 'x'); + } + + public function getExtendedType() + { + return 'foo'; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/TestExtension.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/TestExtension.php new file mode 100644 index 00000000..f9de560f --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/TestExtension.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +use Symfony\Component\Form\FormTypeInterface; +use Symfony\Component\Form\FormTypeExtensionInterface; +use Symfony\Component\Form\FormTypeGuesserInterface; +use Symfony\Component\Form\FormExtensionInterface; + +class TestExtension implements FormExtensionInterface +{ + private $types = array(); + + private $extensions = array(); + + private $guesser; + + public function __construct(FormTypeGuesserInterface $guesser) + { + $this->guesser = $guesser; + } + + public function addType(FormTypeInterface $type) + { + $this->types[$type->getName()] = $type; + } + + public function getType($name) + { + return isset($this->types[$name]) ? $this->types[$name] : null; + } + + public function hasType($name) + { + return isset($this->types[$name]); + } + + public function addTypeExtension(FormTypeExtensionInterface $extension) + { + $type = $extension->getExtendedType(); + + if (!isset($this->extensions[$type])) { + $this->extensions[$type] = array(); + } + + $this->extensions[$type][] = $extension; + } + + public function getTypeExtensions($name) + { + return isset($this->extensions[$name]) ? $this->extensions[$name] : array(); + } + + public function hasTypeExtensions($name) + { + return isset($this->extensions[$name]); + } + + public function getTypeGuesser() + { + return $this->guesser; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/foo b/vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/foo new file mode 100644 index 00000000..e69de29b diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/FormBuilderTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/FormBuilderTest.php new file mode 100644 index 00000000..e076c97e --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/FormBuilderTest.php @@ -0,0 +1,232 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\FormBuilder; + +class FormBuilderTest extends \PHPUnit_Framework_TestCase +{ + private $dispatcher; + + private $factory; + + private $builder; + + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); + $this->builder = new FormBuilder('name', null, $this->dispatcher, $this->factory); + } + + protected function tearDown() + { + $this->dispatcher = null; + $this->factory = null; + $this->builder = null; + } + + /** + * Changing the name is not allowed, otherwise the name and property path + * are not synchronized anymore + * + * @see FormType::buildForm + */ + public function testNoSetName() + { + $this->assertFalse(method_exists($this->builder, 'setName')); + } + + public function testAddNameNoStringAndNoInteger() + { + $this->setExpectedException('Symfony\Component\Form\Exception\UnexpectedTypeException'); + $this->builder->add(true); + } + + public function testAddTypeNoString() + { + $this->setExpectedException('Symfony\Component\Form\Exception\UnexpectedTypeException'); + $this->builder->add('foo', 1234); + } + + public function testAddWithGuessFluent() + { + $this->builder = new FormBuilder('name', 'stdClass', $this->dispatcher, $this->factory); + $builder = $this->builder->add('foo'); + $this->assertSame($builder, $this->builder); + } + + public function testAddIsFluent() + { + $builder = $this->builder->add('foo', 'text', array('bar' => 'baz')); + $this->assertSame($builder, $this->builder); + } + + public function testAdd() + { + $this->assertFalse($this->builder->has('foo')); + $this->builder->add('foo', 'text'); + $this->assertTrue($this->builder->has('foo')); + } + + public function testAddIntegerName() + { + $this->assertFalse($this->builder->has(0)); + $this->builder->add(0, 'text'); + $this->assertTrue($this->builder->has(0)); + } + + public function testAll() + { + $this->factory->expects($this->once()) + ->method('createNamedBuilder') + ->with('foo', 'text') + ->will($this->returnValue(new FormBuilder('foo', null, $this->dispatcher, $this->factory))); + + $this->assertCount(0, $this->builder->all()); + $this->assertFalse($this->builder->has('foo')); + + $this->builder->add('foo', 'text'); + $children = $this->builder->all(); + + $this->assertTrue($this->builder->has('foo')); + $this->assertCount(1, $children); + $this->assertArrayHasKey('foo', $children); + } + + /* + * https://github.com/symfony/symfony/issues/4693 + */ + public function testMaintainOrderOfLazyAndExplicitChildren() + { + $this->builder->add('foo', 'text'); + $this->builder->add($this->getFormBuilder('bar')); + $this->builder->add('baz', 'text'); + + $children = $this->builder->all(); + + $this->assertSame(array('foo', 'bar', 'baz'), array_keys($children)); + } + + public function testAddFormType() + { + $this->assertFalse($this->builder->has('foo')); + $this->builder->add('foo', $this->getMock('Symfony\Component\Form\FormTypeInterface')); + $this->assertTrue($this->builder->has('foo')); + } + + public function testRemove() + { + $this->builder->add('foo', 'text'); + $this->builder->remove('foo'); + $this->assertFalse($this->builder->has('foo')); + } + + public function testRemoveUnknown() + { + $this->builder->remove('foo'); + $this->assertFalse($this->builder->has('foo')); + } + + // https://github.com/symfony/symfony/pull/4826 + public function testRemoveAndGetForm() + { + $this->builder->add('foo', 'text'); + $this->builder->remove('foo'); + $form = $this->builder->getForm(); + $this->assertInstanceOf('Symfony\Component\Form\Form', $form); + } + + public function testCreateNoTypeNo() + { + $this->factory->expects($this->once()) + ->method('createNamedBuilder') + ->with('foo', 'text', null, array()) + ; + + $this->builder->create('foo'); + } + + public function testGetUnknown() + { + $this->setExpectedException('Symfony\Component\Form\Exception\InvalidArgumentException', 'The child with the name "foo" does not exist.'); + $this->builder->get('foo'); + } + + public function testGetExplicitType() + { + $expectedType = 'text'; + $expectedName = 'foo'; + $expectedOptions = array('bar' => 'baz'); + + $this->factory->expects($this->once()) + ->method('createNamedBuilder') + ->with($expectedName, $expectedType, null, $expectedOptions) + ->will($this->returnValue($this->getFormBuilder())); + + $this->builder->add($expectedName, $expectedType, $expectedOptions); + $builder = $this->builder->get($expectedName); + + $this->assertNotSame($builder, $this->builder); + } + + public function testGetGuessedType() + { + $expectedName = 'foo'; + $expectedOptions = array('bar' => 'baz'); + + $this->factory->expects($this->once()) + ->method('createBuilderForProperty') + ->with('stdClass', $expectedName, null, $expectedOptions) + ->will($this->returnValue($this->getFormBuilder())); + + $this->builder = new FormBuilder('name', 'stdClass', $this->dispatcher, $this->factory); + $this->builder->add($expectedName, null, $expectedOptions); + $builder = $this->builder->get($expectedName); + + $this->assertNotSame($builder, $this->builder); + } + + public function testGetFormConfigErasesReferences() + { + $builder = new FormBuilder('name', null, $this->dispatcher, $this->factory); + $builder->add(new FormBuilder('child', null, $this->dispatcher, $this->factory)); + + $config = $builder->getFormConfig(); + $reflClass = new \ReflectionClass($config); + $children = $reflClass->getProperty('children'); + $unresolvedChildren = $reflClass->getProperty('unresolvedChildren'); + + $children->setAccessible(true); + $unresolvedChildren->setAccessible(true); + + $this->assertEmpty($children->getValue($config)); + $this->assertEmpty($unresolvedChildren->getValue($config)); + } + + private function getFormBuilder($name = 'name') + { + $mock = $this->getMockBuilder('Symfony\Component\Form\FormBuilder') + ->disableOriginalConstructor() + ->getMock(); + + $mock->expects($this->any()) + ->method('getName') + ->will($this->returnValue($name)); + + return $mock; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/FormConfigTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/FormConfigTest.php new file mode 100644 index 00000000..961dfd33 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/FormConfigTest.php @@ -0,0 +1,148 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\Form\FormConfigBuilder; +use Symfony\Component\Form\Exception\InvalidArgumentException; + +/** + * @author Bernhard Schussek + */ +class FormConfigTest extends \PHPUnit_Framework_TestCase +{ + public function getHtml4Ids() + { + return array( + array('z0', true), + array('A0', true), + array('A9', true), + array('Z0', true), + array('#', false), + array('a#', false), + array('a$', false), + array('a%', false), + array('a ', false), + array("a\t", false), + array("a\n", false), + array('a-', true), + array('a_', true), + array('a:', true), + // Periods are allowed by the HTML4 spec, but disallowed by us + // because they break the generated property paths + array('a.', false), + // Contrary to the HTML4 spec, we allow names starting with a + // number, otherwise naming fields by collection indices is not + // possible. + // For root forms, leading digits will be stripped from the + // "id" attribute to produce valid HTML4. + array('0', true), + array('9', true), + // Contrary to the HTML4 spec, we allow names starting with an + // underscore, since this is already a widely used practice in + // Symfony2. + // For root forms, leading underscores will be stripped from the + // "id" attribute to produce valid HTML4. + array('_', true), + // Integers are allowed + array(0, true), + array(123, true), + // NULL is allowed + array(null, true), + // Other types are not + array(1.23, false), + array(5., false), + array(true, false), + array(new \stdClass(), false), + ); + } + + /** + * @dataProvider getHtml4Ids + */ + public function testNameAcceptsOnlyNamesValidAsIdsInHtml4($name, $accepted) + { + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + + try { + new FormConfigBuilder($name, null, $dispatcher); + if (!$accepted) { + $this->fail(sprintf('The value "%s" should not be accepted', $name)); + } + } catch (UnexpectedTypeException $e) { + // if the value was not accepted, but should be, rethrow exception + if ($accepted) { + throw $e; + } + } catch (InvalidArgumentException $e) { + // if the value was not accepted, but should be, rethrow exception + if ($accepted) { + throw $e; + } + } + } + + public function testGetRequestHandlerCreatesNativeRequestHandlerIfNotSet() + { + $config = $this->getConfigBuilder()->getFormConfig(); + + $this->assertInstanceOf('Symfony\Component\Form\NativeRequestHandler', $config->getRequestHandler()); + } + + public function testGetRequestHandlerReusesNativeRequestHandlerInstance() + { + $config1 = $this->getConfigBuilder()->getFormConfig(); + $config2 = $this->getConfigBuilder()->getFormConfig(); + + $this->assertSame($config1->getRequestHandler(), $config2->getRequestHandler()); + } + + public function testSetMethodAllowsGet() + { + $this->getConfigBuilder()->setMethod('GET'); + } + + public function testSetMethodAllowsPost() + { + $this->getConfigBuilder()->setMethod('POST'); + } + + public function testSetMethodAllowsPut() + { + $this->getConfigBuilder()->setMethod('PUT'); + } + + public function testSetMethodAllowsDelete() + { + $this->getConfigBuilder()->setMethod('DELETE'); + } + + public function testSetMethodAllowsPatch() + { + $this->getConfigBuilder()->setMethod('PATCH'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException + */ + public function testSetMethodDoesNotAllowOtherValues() + { + $this->getConfigBuilder()->setMethod('foo'); + } + + private function getConfigBuilder($name = 'name') + { + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + + return new FormConfigBuilder($name, null, $dispatcher); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/FormFactoryBuilderTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/FormFactoryBuilderTest.php new file mode 100644 index 00000000..a1292dbe --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/FormFactoryBuilderTest.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\FormFactoryBuilder; +use Symfony\Component\Form\Tests\Fixtures\FooType; + +class FormFactoryBuilderTest extends \PHPUnit_Framework_TestCase +{ + private $registry; + private $guesser; + private $type; + + protected function setUp() + { + $factory = new \ReflectionClass('Symfony\Component\Form\FormFactory'); + $this->registry = $factory->getProperty('registry'); + $this->registry->setAccessible(true); + + $this->guesser = $this->getMock('Symfony\Component\Form\FormTypeGuesserInterface'); + $this->type = new FooType; + } + + public function testAddType() + { + $factoryBuilder = new FormFactoryBuilder; + $factoryBuilder->addType($this->type); + + $factory = $factoryBuilder->getFormFactory(); + $registry = $this->registry->getValue($factory); + $extensions = $registry->getExtensions(); + + $this->assertCount(1, $extensions); + $this->assertTrue($extensions[0]->hasType($this->type->getName())); + $this->assertNull($extensions[0]->getTypeGuesser()); + } + + public function testAddTypeGuesser() + { + $factoryBuilder = new FormFactoryBuilder; + $factoryBuilder->addTypeGuesser($this->guesser); + + $factory = $factoryBuilder->getFormFactory(); + $registry = $this->registry->getValue($factory); + $extensions = $registry->getExtensions(); + + $this->assertCount(1, $extensions); + $this->assertNotNull($extensions[0]->getTypeGuesser()); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/FormFactoryTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/FormFactoryTest.php new file mode 100644 index 00000000..ea872b01 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/FormFactoryTest.php @@ -0,0 +1,506 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\FormTypeGuesserChain; +use Symfony\Component\Form\FormFactory; +use Symfony\Component\Form\Guess\Guess; +use Symfony\Component\Form\Guess\ValueGuess; +use Symfony\Component\Form\Guess\TypeGuess; +use Symfony\Component\Form\Tests\Fixtures\Author; +use Symfony\Component\Form\Tests\Fixtures\FooType; +use Symfony\Component\Form\Tests\Fixtures\FooSubType; +use Symfony\Component\Form\Tests\Fixtures\FooSubTypeWithParentInstance; + +/** + * @author Bernhard Schussek + */ +class FormFactoryTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $guesser1; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $guesser2; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $registry; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $resolvedTypeFactory; + + /** + * @var FormFactory + */ + private $factory; + + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + $this->resolvedTypeFactory = $this->getMock('Symfony\Component\Form\ResolvedFormTypeFactoryInterface'); + $this->guesser1 = $this->getMock('Symfony\Component\Form\FormTypeGuesserInterface'); + $this->guesser2 = $this->getMock('Symfony\Component\Form\FormTypeGuesserInterface'); + $this->registry = $this->getMock('Symfony\Component\Form\FormRegistryInterface'); + $this->factory = new FormFactory($this->registry, $this->resolvedTypeFactory); + + $this->registry->expects($this->any()) + ->method('getTypeGuesser') + ->will($this->returnValue(new FormTypeGuesserChain(array( + $this->guesser1, + $this->guesser2, + )))); + } + + public function testCreateNamedBuilderWithTypeName() + { + $options = array('a' => '1', 'b' => '2'); + $resolvedType = $this->getMockResolvedType(); + + $this->registry->expects($this->once()) + ->method('getType') + ->with('type') + ->will($this->returnValue($resolvedType)); + + $resolvedType->expects($this->once()) + ->method('createBuilder') + ->with($this->factory, 'name', $options) + ->will($this->returnValue('BUILDER')); + + $this->assertSame('BUILDER', $this->factory->createNamedBuilder('name', 'type', null, $options)); + } + + public function testCreateNamedBuilderWithTypeInstance() + { + $options = array('a' => '1', 'b' => '2'); + $type = new FooType(); + $resolvedType = $this->getMockResolvedType(); + + $this->resolvedTypeFactory->expects($this->once()) + ->method('createResolvedType') + ->with($type) + ->will($this->returnValue($resolvedType)); + + $resolvedType->expects($this->once()) + ->method('createBuilder') + ->with($this->factory, 'name', $options) + ->will($this->returnValue('BUILDER')); + + $this->assertSame('BUILDER', $this->factory->createNamedBuilder('name', $type, null, $options)); + } + + public function testCreateNamedBuilderWithTypeInstanceWithParentType() + { + $options = array('a' => '1', 'b' => '2'); + $type = new FooSubType(); + $resolvedType = $this->getMockResolvedType(); + $parentResolvedType = $this->getMockResolvedType(); + + $this->registry->expects($this->once()) + ->method('getType') + ->with('foo') + ->will($this->returnValue($parentResolvedType)); + + $this->resolvedTypeFactory->expects($this->once()) + ->method('createResolvedType') + ->with($type, array(), $parentResolvedType) + ->will($this->returnValue($resolvedType)); + + $resolvedType->expects($this->once()) + ->method('createBuilder') + ->with($this->factory, 'name', $options) + ->will($this->returnValue('BUILDER')); + + $this->assertSame('BUILDER', $this->factory->createNamedBuilder('name', $type, null, $options)); + } + + public function testCreateNamedBuilderWithTypeInstanceWithParentTypeInstance() + { + $options = array('a' => '1', 'b' => '2'); + $type = new FooSubTypeWithParentInstance(); + $resolvedType = $this->getMockResolvedType(); + $parentResolvedType = $this->getMockResolvedType(); + + $this->resolvedTypeFactory->expects($this->at(0)) + ->method('createResolvedType') + ->with($type->getParent()) + ->will($this->returnValue($parentResolvedType)); + + $this->resolvedTypeFactory->expects($this->at(1)) + ->method('createResolvedType') + ->with($type, array(), $parentResolvedType) + ->will($this->returnValue($resolvedType)); + + $resolvedType->expects($this->once()) + ->method('createBuilder') + ->with($this->factory, 'name', $options) + ->will($this->returnValue('BUILDER')); + + $this->assertSame('BUILDER', $this->factory->createNamedBuilder('name', $type, null, $options)); + } + + public function testCreateNamedBuilderWithResolvedTypeInstance() + { + $options = array('a' => '1', 'b' => '2'); + $resolvedType = $this->getMockResolvedType(); + + $resolvedType->expects($this->once()) + ->method('createBuilder') + ->with($this->factory, 'name', $options) + ->will($this->returnValue('BUILDER')); + + $this->assertSame('BUILDER', $this->factory->createNamedBuilder('name', $resolvedType, null, $options)); + } + + public function testCreateNamedBuilderFillsDataOption() + { + $givenOptions = array('a' => '1', 'b' => '2'); + $expectedOptions = array_merge($givenOptions, array('data' => 'DATA')); + $resolvedType = $this->getMockResolvedType(); + + $this->registry->expects($this->once()) + ->method('getType') + ->with('type') + ->will($this->returnValue($resolvedType)); + + $resolvedType->expects($this->once()) + ->method('createBuilder') + ->with($this->factory, 'name', $expectedOptions) + ->will($this->returnValue('BUILDER')); + + $this->assertSame('BUILDER', $this->factory->createNamedBuilder('name', 'type', 'DATA', $givenOptions)); + } + + public function testCreateNamedBuilderDoesNotOverrideExistingDataOption() + { + $options = array('a' => '1', 'b' => '2', 'data' => 'CUSTOM'); + $resolvedType = $this->getMockResolvedType(); + + $this->registry->expects($this->once()) + ->method('getType') + ->with('type') + ->will($this->returnValue($resolvedType)); + + $resolvedType->expects($this->once()) + ->method('createBuilder') + ->with($this->factory, 'name', $options) + ->will($this->returnValue('BUILDER')); + + $this->assertSame('BUILDER', $this->factory->createNamedBuilder('name', 'type', 'DATA', $options)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + * @expectedExceptionMessage Expected argument of type "string, Symfony\Component\Form\ResolvedFormTypeInterface or Symfony\Component\Form\FormTypeInterface", "stdClass" given + */ + public function testCreateNamedBuilderThrowsUnderstandableException() + { + $this->factory->createNamedBuilder('name', new \stdClass()); + } + + public function testCreateUsesTypeNameIfTypeGivenAsString() + { + $options = array('a' => '1', 'b' => '2'); + $resolvedType = $this->getMockResolvedType(); + $builder = $this->getMockFormBuilder(); + + $this->registry->expects($this->once()) + ->method('getType') + ->with('TYPE') + ->will($this->returnValue($resolvedType)); + + $resolvedType->expects($this->once()) + ->method('createBuilder') + ->with($this->factory, 'TYPE', $options) + ->will($this->returnValue($builder)); + + $builder->expects($this->once()) + ->method('getForm') + ->will($this->returnValue('FORM')); + + $this->assertSame('FORM', $this->factory->create('TYPE', null, $options)); + } + + public function testCreateUsesTypeNameIfTypeGivenAsObject() + { + $options = array('a' => '1', 'b' => '2'); + $resolvedType = $this->getMockResolvedType(); + $builder = $this->getMockFormBuilder(); + + $resolvedType->expects($this->once()) + ->method('getName') + ->will($this->returnValue('TYPE')); + + $resolvedType->expects($this->once()) + ->method('createBuilder') + ->with($this->factory, 'TYPE', $options) + ->will($this->returnValue($builder)); + + $builder->expects($this->once()) + ->method('getForm') + ->will($this->returnValue('FORM')); + + $this->assertSame('FORM', $this->factory->create($resolvedType, null, $options)); + } + + public function testCreateNamed() + { + $options = array('a' => '1', 'b' => '2'); + $resolvedType = $this->getMockResolvedType(); + $builder = $this->getMockFormBuilder(); + + $this->registry->expects($this->once()) + ->method('getType') + ->with('type') + ->will($this->returnValue($resolvedType)); + + $resolvedType->expects($this->once()) + ->method('createBuilder') + ->with($this->factory, 'name', $options) + ->will($this->returnValue($builder)); + + $builder->expects($this->once()) + ->method('getForm') + ->will($this->returnValue('FORM')); + + $this->assertSame('FORM', $this->factory->createNamed('name', 'type', null, $options)); + } + + public function testCreateBuilderForPropertyWithoutTypeGuesser() + { + $registry = $this->getMock('Symfony\Component\Form\FormRegistryInterface'); + $factory = $this->getMockBuilder('Symfony\Component\Form\FormFactory') + ->setMethods(array('createNamedBuilder')) + ->setConstructorArgs(array($registry, $this->resolvedTypeFactory)) + ->getMock(); + + $factory->expects($this->once()) + ->method('createNamedBuilder') + ->with('firstName', 'text', null, array()) + ->will($this->returnValue('builderInstance')); + + $builder = $factory->createBuilderForProperty('Application\Author', 'firstName'); + + $this->assertEquals('builderInstance', $builder); + } + + public function testCreateBuilderForPropertyCreatesFormWithHighestConfidence() + { + $this->guesser1->expects($this->once()) + ->method('guessType') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(new TypeGuess( + 'text', + array('max_length' => 10), + Guess::MEDIUM_CONFIDENCE + ))); + + $this->guesser2->expects($this->once()) + ->method('guessType') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(new TypeGuess( + 'password', + array('max_length' => 7), + Guess::HIGH_CONFIDENCE + ))); + + $factory = $this->getMockFactory(array('createNamedBuilder')); + + $factory->expects($this->once()) + ->method('createNamedBuilder') + ->with('firstName', 'password', null, array('max_length' => 7)) + ->will($this->returnValue('builderInstance')); + + $builder = $factory->createBuilderForProperty('Application\Author', 'firstName'); + + $this->assertEquals('builderInstance', $builder); + } + + public function testCreateBuilderCreatesTextFormIfNoGuess() + { + $this->guesser1->expects($this->once()) + ->method('guessType') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(null)); + + $factory = $this->getMockFactory(array('createNamedBuilder')); + + $factory->expects($this->once()) + ->method('createNamedBuilder') + ->with('firstName', 'text') + ->will($this->returnValue('builderInstance')); + + $builder = $factory->createBuilderForProperty('Application\Author', 'firstName'); + + $this->assertEquals('builderInstance', $builder); + } + + public function testOptionsCanBeOverridden() + { + $this->guesser1->expects($this->once()) + ->method('guessType') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(new TypeGuess( + 'text', + array('max_length' => 10), + Guess::MEDIUM_CONFIDENCE + ))); + + $factory = $this->getMockFactory(array('createNamedBuilder')); + + $factory->expects($this->once()) + ->method('createNamedBuilder') + ->with('firstName', 'text', null, array('max_length' => 11)) + ->will($this->returnValue('builderInstance')); + + $builder = $factory->createBuilderForProperty( + 'Application\Author', + 'firstName', + null, + array('max_length' => 11) + ); + + $this->assertEquals('builderInstance', $builder); + } + + public function testCreateBuilderUsesMaxLengthIfFound() + { + $this->guesser1->expects($this->once()) + ->method('guessMaxLength') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(new ValueGuess( + 15, + Guess::MEDIUM_CONFIDENCE + ))); + + $this->guesser2->expects($this->once()) + ->method('guessMaxLength') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(new ValueGuess( + 20, + Guess::HIGH_CONFIDENCE + ))); + + $factory = $this->getMockFactory(array('createNamedBuilder')); + + $factory->expects($this->once()) + ->method('createNamedBuilder') + ->with('firstName', 'text', null, array('max_length' => 20)) + ->will($this->returnValue('builderInstance')); + + $builder = $factory->createBuilderForProperty( + 'Application\Author', + 'firstName' + ); + + $this->assertEquals('builderInstance', $builder); + } + + public function testCreateBuilderUsesRequiredSettingWithHighestConfidence() + { + $this->guesser1->expects($this->once()) + ->method('guessRequired') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(new ValueGuess( + true, + Guess::MEDIUM_CONFIDENCE + ))); + + $this->guesser2->expects($this->once()) + ->method('guessRequired') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(new ValueGuess( + false, + Guess::HIGH_CONFIDENCE + ))); + + $factory = $this->getMockFactory(array('createNamedBuilder')); + + $factory->expects($this->once()) + ->method('createNamedBuilder') + ->with('firstName', 'text', null, array('required' => false)) + ->will($this->returnValue('builderInstance')); + + $builder = $factory->createBuilderForProperty( + 'Application\Author', + 'firstName' + ); + + $this->assertEquals('builderInstance', $builder); + } + + public function testCreateBuilderUsesPatternIfFound() + { + $this->guesser1->expects($this->once()) + ->method('guessPattern') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(new ValueGuess( + '[a-z]', + Guess::MEDIUM_CONFIDENCE + ))); + + $this->guesser2->expects($this->once()) + ->method('guessPattern') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(new ValueGuess( + '[a-zA-Z]', + Guess::HIGH_CONFIDENCE + ))); + + $factory = $this->getMockFactory(array('createNamedBuilder')); + + $factory->expects($this->once()) + ->method('createNamedBuilder') + ->with('firstName', 'text', null, array('pattern' => '[a-zA-Z]')) + ->will($this->returnValue('builderInstance')); + + $builder = $factory->createBuilderForProperty( + 'Application\Author', + 'firstName' + ); + + $this->assertEquals('builderInstance', $builder); + } + + private function getMockFactory(array $methods = array()) + { + return $this->getMockBuilder('Symfony\Component\Form\FormFactory') + ->setMethods($methods) + ->setConstructorArgs(array($this->registry, $this->resolvedTypeFactory)) + ->getMock(); + } + + private function getMockResolvedType() + { + return $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + } + + private function getMockType() + { + return $this->getMock('Symfony\Component\Form\FormTypeInterface'); + } + + private function getMockFormBuilder() + { + return $this->getMock('Symfony\Component\Form\Test\FormBuilderInterface'); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/FormIntegrationTestCase.php b/vendor/symfony/form/Symfony/Component/Form/Tests/FormIntegrationTestCase.php new file mode 100644 index 00000000..763286c2 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/FormIntegrationTestCase.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\Test\FormIntegrationTestCase as BaseFormIntegrationTestCase; + +/** + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use Symfony\Component\Form\Test\FormIntegrationTestCase instead. + */ +abstract class FormIntegrationTestCase extends BaseFormIntegrationTestCase +{ +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/FormPerformanceTestCase.php b/vendor/symfony/form/Symfony/Component/Form/Tests/FormPerformanceTestCase.php new file mode 100644 index 00000000..39882e85 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/FormPerformanceTestCase.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\Test\FormPerformanceTestCase as BaseFormPerformanceTestCase; + +/** + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use Symfony\Component\Form\Test\FormPerformanceTestCase instead. + */ +abstract class FormPerformanceTestCase extends BaseFormPerformanceTestCase +{ +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/FormRegistryTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/FormRegistryTest.php new file mode 100644 index 00000000..0c8bb6b4 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/FormRegistryTest.php @@ -0,0 +1,243 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\FormRegistry; +use Symfony\Component\Form\FormTypeGuesserChain; +use Symfony\Component\Form\Tests\Fixtures\TestExtension; +use Symfony\Component\Form\Tests\Fixtures\FooSubTypeWithParentInstance; +use Symfony\Component\Form\Tests\Fixtures\FooSubType; +use Symfony\Component\Form\Tests\Fixtures\FooTypeBazExtension; +use Symfony\Component\Form\Tests\Fixtures\FooTypeBarExtension; +use Symfony\Component\Form\Tests\Fixtures\FooType; + +/** + * @author Bernhard Schussek + */ +class FormRegistryTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var FormRegistry + */ + private $registry; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $resolvedTypeFactory; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $guesser1; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $guesser2; + + /** + * @var TestExtension + */ + private $extension1; + + /** + * @var TestExtension + */ + private $extension2; + + protected function setUp() + { + $this->resolvedTypeFactory = $this->getMock('Symfony\Component\Form\ResolvedFormTypeFactory'); + $this->guesser1 = $this->getMock('Symfony\Component\Form\FormTypeGuesserInterface'); + $this->guesser2 = $this->getMock('Symfony\Component\Form\FormTypeGuesserInterface'); + $this->extension1 = new TestExtension($this->guesser1); + $this->extension2 = new TestExtension($this->guesser2); + $this->registry = new FormRegistry(array( + $this->extension1, + $this->extension2, + ), $this->resolvedTypeFactory); + } + + public function testGetTypeFromExtension() + { + $type = new FooType(); + $resolvedType = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + + $this->extension2->addType($type); + + $this->resolvedTypeFactory->expects($this->once()) + ->method('createResolvedType') + ->with($type) + ->will($this->returnValue($resolvedType)); + + $resolvedType->expects($this->any()) + ->method('getName') + ->will($this->returnValue('foo')); + + $resolvedType = $this->registry->getType('foo'); + + $this->assertSame($resolvedType, $this->registry->getType('foo')); + } + + public function testGetTypeWithTypeExtensions() + { + $type = new FooType(); + $ext1 = new FooTypeBarExtension(); + $ext2 = new FooTypeBazExtension(); + $resolvedType = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + + $this->extension2->addType($type); + $this->extension1->addTypeExtension($ext1); + $this->extension2->addTypeExtension($ext2); + + $this->resolvedTypeFactory->expects($this->once()) + ->method('createResolvedType') + ->with($type, array($ext1, $ext2)) + ->will($this->returnValue($resolvedType)); + + $resolvedType->expects($this->any()) + ->method('getName') + ->will($this->returnValue('foo')); + + $this->assertSame($resolvedType, $this->registry->getType('foo')); + } + + public function testGetTypeConnectsParent() + { + $parentType = new FooType(); + $type = new FooSubType(); + $parentResolvedType = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + $resolvedType = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + + $this->extension1->addType($parentType); + $this->extension2->addType($type); + + $this->resolvedTypeFactory->expects($this->at(0)) + ->method('createResolvedType') + ->with($parentType) + ->will($this->returnValue($parentResolvedType)); + + $this->resolvedTypeFactory->expects($this->at(1)) + ->method('createResolvedType') + ->with($type, array(), $parentResolvedType) + ->will($this->returnValue($resolvedType)); + + $parentResolvedType->expects($this->any()) + ->method('getName') + ->will($this->returnValue('foo')); + + $resolvedType->expects($this->any()) + ->method('getName') + ->will($this->returnValue('foo_sub_type')); + + $this->assertSame($resolvedType, $this->registry->getType('foo_sub_type')); + } + + public function testGetTypeConnectsParentIfGetParentReturnsInstance() + { + $type = new FooSubTypeWithParentInstance(); + $parentResolvedType = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + $resolvedType = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + + $this->extension1->addType($type); + + $this->resolvedTypeFactory->expects($this->at(0)) + ->method('createResolvedType') + ->with($this->isInstanceOf('Symfony\Component\Form\Tests\Fixtures\FooType')) + ->will($this->returnValue($parentResolvedType)); + + $this->resolvedTypeFactory->expects($this->at(1)) + ->method('createResolvedType') + ->with($type, array(), $parentResolvedType) + ->will($this->returnValue($resolvedType)); + + $parentResolvedType->expects($this->any()) + ->method('getName') + ->will($this->returnValue('foo')); + + $resolvedType->expects($this->any()) + ->method('getName') + ->will($this->returnValue('foo_sub_type_parent_instance')); + + $this->assertSame($resolvedType, $this->registry->getType('foo_sub_type_parent_instance')); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + */ + public function testGetTypeThrowsExceptionIfParentNotFound() + { + $type = new FooSubType(); + + $this->extension1->addType($type); + + $this->registry->getType($type); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException + */ + public function testGetTypeThrowsExceptionIfTypeNotFound() + { + $this->registry->getType('bar'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + */ + public function testGetTypeThrowsExceptionIfNoString() + { + $this->registry->getType(array()); + } + + public function testHasTypeAfterLoadingFromExtension() + { + $type = new FooType(); + $resolvedType = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + + $this->resolvedTypeFactory->expects($this->once()) + ->method('createResolvedType') + ->with($type) + ->will($this->returnValue($resolvedType)); + + $resolvedType->expects($this->any()) + ->method('getName') + ->will($this->returnValue('foo')); + + $this->assertFalse($this->registry->hasType('foo')); + + $this->extension2->addType($type); + + $this->assertTrue($this->registry->hasType('foo')); + } + + public function testGetTypeGuesser() + { + $expectedGuesser = new FormTypeGuesserChain(array($this->guesser1, $this->guesser2)); + + $this->assertEquals($expectedGuesser, $this->registry->getTypeGuesser()); + + $registry = new FormRegistry( + array($this->getMock('Symfony\Component\Form\FormExtensionInterface')), + $this->resolvedTypeFactory); + + $this->assertNull($registry->getTypeGuesser()); + } + + public function testGetExtensions() + { + $expectedExtensions = array($this->extension1, $this->extension2); + + $this->assertEquals($expectedExtensions, $this->registry->getExtensions()); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/FormRendererTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/FormRendererTest.php new file mode 100644 index 00000000..69b048f7 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/FormRendererTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Test; + +class FormRendererTest extends \PHPUnit_Framework_TestCase +{ + public function testHumanize() + { + $renderer = $this->getMockBuilder('Symfony\Component\Form\FormRenderer') + ->setMethods(null) + ->disableOriginalConstructor() + ->getMock() + ; + + $this->assertEquals('Is active', $renderer->humanize('is_active')); + $this->assertEquals('Is active', $renderer->humanize('isActive')); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/Guess/GuessTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/Guess/GuessTest.php new file mode 100644 index 00000000..235eb6ed --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/Guess/GuessTest.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Guess; + +use Symfony\Component\Form\Guess\Guess; + +class TestGuess extends Guess {} + +class GuessTest extends \PHPUnit_Framework_TestCase +{ + public function testGetBestGuessReturnsGuessWithHighestConfidence() + { + $guess1 = new TestGuess(Guess::MEDIUM_CONFIDENCE); + $guess2 = new TestGuess(Guess::LOW_CONFIDENCE); + $guess3 = new TestGuess(Guess::HIGH_CONFIDENCE); + + $this->assertSame($guess3, Guess::getBestGuess(array($guess1, $guess2, $guess3))); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testGuessExpectsValidConfidence() + { + new TestGuess(5); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/NativeRequestHandlerTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/NativeRequestHandlerTest.php new file mode 100644 index 00000000..9d3a997f --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/NativeRequestHandlerTest.php @@ -0,0 +1,219 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\NativeRequestHandler; + +/** + * @author Bernhard Schussek + */ +class NativeRequestHandlerTest extends AbstractRequestHandlerTest +{ + private static $serverBackup; + + public static function setUpBeforeClass() + { + self::$serverBackup = $_SERVER; + } + + protected function setUp() + { + parent::setUp(); + + $_GET = array(); + $_POST = array(); + $_FILES = array(); + $_SERVER = array( + // PHPUnit needs this entry + 'SCRIPT_NAME' => self::$serverBackup['SCRIPT_NAME'], + ); + } + + protected function tearDown() + { + parent::tearDown(); + + $_GET = array(); + $_POST = array(); + $_FILES = array(); + $_SERVER = self::$serverBackup; + } + + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + */ + public function testRequestShouldBeNull() + { + $this->requestHandler->handleRequest($this->getMockForm('name', 'GET'), 'request'); + } + + public function testMethodOverrideHeaderTakesPrecedenceIfPost() + { + $form = $this->getMockForm('param1', 'PUT'); + + $this->setRequestData('POST', array( + 'param1' => 'DATA', + )); + + $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] = 'PUT'; + + $form->expects($this->once()) + ->method('submit') + ->with('DATA'); + + $this->requestHandler->handleRequest($form, $this->request); + } + + public function testConvertEmptyUploadedFilesToNull() + { + $form = $this->getMockForm('param1', 'POST', false); + + $this->setRequestData('POST', array(), array('param1' => array( + 'name' => '', + 'type' => '', + 'tmp_name' => '', + 'error' => UPLOAD_ERR_NO_FILE, + 'size' => 0 + ))); + + $form->expects($this->once()) + ->method('submit') + ->with($this->identicalTo(null)); + + $this->requestHandler->handleRequest($form, $this->request); + } + + public function testFixBuggyFilesArray() + { + $form = $this->getMockForm('param1', 'POST', false); + + $this->setRequestData('POST', array(), array('param1' => array( + 'name' => array( + 'field' => 'upload.txt', + ), + 'type' => array( + 'field' => 'text/plain', + ), + 'tmp_name' => array( + 'field' => 'owfdskjasdfsa', + ), + 'error' => array( + 'field' => UPLOAD_ERR_OK, + ), + 'size' => array( + 'field' => 100, + ), + ))); + + $form->expects($this->once()) + ->method('submit') + ->with(array( + 'field' => array( + 'name' => 'upload.txt', + 'type' => 'text/plain', + 'tmp_name' => 'owfdskjasdfsa', + 'error' => UPLOAD_ERR_OK, + 'size' => 100, + ), + )); + + $this->requestHandler->handleRequest($form, $this->request); + } + + public function testFixBuggyNestedFilesArray() + { + $form = $this->getMockForm('param1', 'POST'); + + $this->setRequestData('POST', array(), array('param1' => array( + 'name' => array( + 'field' => array('subfield' => 'upload.txt'), + ), + 'type' => array( + 'field' => array('subfield' => 'text/plain'), + ), + 'tmp_name' => array( + 'field' => array('subfield' => 'owfdskjasdfsa'), + ), + 'error' => array( + 'field' => array('subfield' => UPLOAD_ERR_OK), + ), + 'size' => array( + 'field' => array('subfield' => 100), + ), + ))); + + $form->expects($this->once()) + ->method('submit') + ->with(array( + 'field' => array( + 'subfield' => array( + 'name' => 'upload.txt', + 'type' => 'text/plain', + 'tmp_name' => 'owfdskjasdfsa', + 'error' => UPLOAD_ERR_OK, + 'size' => 100, + ), + ), + )); + + $this->requestHandler->handleRequest($form, $this->request); + } + + public function testMethodOverrideHeaderIgnoredIfNotPost() + { + $form = $this->getMockForm('param1', 'POST'); + + $this->setRequestData('GET', array( + 'param1' => 'DATA', + )); + + $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] = 'PUT'; + + $form->expects($this->never()) + ->method('submit'); + + $this->requestHandler->handleRequest($form, $this->request); + } + + protected function setRequestData($method, $data, $files = array()) + { + if ('GET' === $method) { + $_GET = $data; + $_FILES = array(); + } else { + $_POST = $data; + $_FILES = $files; + } + + $_SERVER = array( + 'REQUEST_METHOD' => $method, + // PHPUnit needs this entry + 'SCRIPT_NAME' => self::$serverBackup['SCRIPT_NAME'], + ); + } + + protected function getRequestHandler() + { + return new NativeRequestHandler(); + } + + protected function getMockFile() + { + return array( + 'name' => 'upload.txt', + 'type' => 'text/plain', + 'tmp_name' => 'owfdskjasdfsa', + 'error' => UPLOAD_ERR_OK, + 'size' => 100, + ); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/ResolvedFormTypeTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/ResolvedFormTypeTest.php new file mode 100644 index 00000000..bb32a241 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/ResolvedFormTypeTest.php @@ -0,0 +1,280 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\ResolvedFormType; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Form\Form; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +/** + * @author Bernhard Schussek + */ +class ResolvedFormTypeTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $dispatcher; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $factory; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $dataMapper; + + protected function setUp() + { + if (!class_exists('Symfony\Component\OptionsResolver\OptionsResolver')) { + $this->markTestSkipped('The "OptionsResolver" component is not available'); + } + + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); + $this->dataMapper = $this->getMock('Symfony\Component\Form\DataMapperInterface'); + } + + public function testCreateBuilder() + { + if (version_compare(\PHPUnit_Runner_Version::id(), '3.7', '<')) { + $this->markTestSkipped('This test requires PHPUnit 3.7.'); + } + + $parentType = $this->getMockFormType(); + $type = $this->getMockFormType(); + $extension1 = $this->getMockFormTypeExtension(); + $extension2 = $this->getMockFormTypeExtension(); + + $parentResolvedType = new ResolvedFormType($parentType); + $resolvedType = new ResolvedFormType($type, array($extension1, $extension2), $parentResolvedType); + + $test = $this; + $i = 0; + + $assertIndex = function ($index) use (&$i, $test) { + return function () use (&$i, $test, $index) { + /* @var \PHPUnit_Framework_TestCase $test */ + $test->assertEquals($index, $i, 'Executed at index '.$index); + + ++$i; + }; + }; + + $assertIndexAndAddOption = function ($index, $option, $default) use ($assertIndex) { + $assertIndex = $assertIndex($index); + + return function (OptionsResolverInterface $resolver) use ($assertIndex, $index, $option, $default) { + $assertIndex(); + + $resolver->setDefaults(array($option => $default)); + }; + }; + + // First the default options are generated for the super type + $parentType->expects($this->once()) + ->method('setDefaultOptions') + ->will($this->returnCallback($assertIndexAndAddOption(0, 'a', 'a_default'))); + + // The form type itself + $type->expects($this->once()) + ->method('setDefaultOptions') + ->will($this->returnCallback($assertIndexAndAddOption(1, 'b', 'b_default'))); + + // And its extensions + $extension1->expects($this->once()) + ->method('setDefaultOptions') + ->will($this->returnCallback($assertIndexAndAddOption(2, 'c', 'c_default'))); + + $extension2->expects($this->once()) + ->method('setDefaultOptions') + ->will($this->returnCallback($assertIndexAndAddOption(3, 'd', 'd_default'))); + + $givenOptions = array('a' => 'a_custom', 'c' => 'c_custom'); + $resolvedOptions = array('a' => 'a_custom', 'b' => 'b_default', 'c' => 'c_custom', 'd' => 'd_default'); + + // Then the form is built for the super type + $parentType->expects($this->once()) + ->method('buildForm') + ->with($this->anything(), $resolvedOptions) + ->will($this->returnCallback($assertIndex(4))); + + // Then the type itself + $type->expects($this->once()) + ->method('buildForm') + ->with($this->anything(), $resolvedOptions) + ->will($this->returnCallback($assertIndex(5))); + + // Then its extensions + $extension1->expects($this->once()) + ->method('buildForm') + ->with($this->anything(), $resolvedOptions) + ->will($this->returnCallback($assertIndex(6))); + + $extension2->expects($this->once()) + ->method('buildForm') + ->with($this->anything(), $resolvedOptions) + ->will($this->returnCallback($assertIndex(7))); + + $factory = $this->getMockFormFactory(); + $builder = $resolvedType->createBuilder($factory, 'name', $givenOptions); + + $this->assertSame($resolvedType, $builder->getType()); + } + + public function testCreateView() + { + $parentType = $this->getMockFormType(); + $type = $this->getMockFormType(); + $field1Type = $this->getMockFormType(); + $field2Type = $this->getMockFormType(); + $extension1 = $this->getMockFormTypeExtension(); + $extension2 = $this->getMockFormTypeExtension(); + + $parentResolvedType = new ResolvedFormType($parentType); + $resolvedType = new ResolvedFormType($type, array($extension1, $extension2), $parentResolvedType); + $field1ResolvedType = new ResolvedFormType($field1Type); + $field2ResolvedType = new ResolvedFormType($field2Type); + + $options = array('a' => '1', 'b' => '2'); + $form = $this->getBuilder('name', $options) + ->setCompound(true) + ->setDataMapper($this->dataMapper) + ->setType($resolvedType) + ->add($this->getBuilder('foo')->setType($field1ResolvedType)) + ->add($this->getBuilder('bar')->setType($field2ResolvedType)) + ->getForm(); + + $test = $this; + $i = 0; + + $assertIndexAndNbOfChildViews = function ($index, $nbOfChildViews) use (&$i, $test) { + return function (FormView $view) use (&$i, $test, $index, $nbOfChildViews) { + /* @var \PHPUnit_Framework_TestCase $test */ + $test->assertEquals($index, $i, 'Executed at index '.$index); + $test->assertCount($nbOfChildViews, $view); + + ++$i; + }; + }; + + // First the super type + $parentType->expects($this->once()) + ->method('buildView') + ->with($this->anything(), $form, $options) + ->will($this->returnCallback($assertIndexAndNbOfChildViews(0, 0))); + + // Then the type itself + $type->expects($this->once()) + ->method('buildView') + ->with($this->anything(), $form, $options) + ->will($this->returnCallback($assertIndexAndNbOfChildViews(1, 0))); + + // Then its extensions + $extension1->expects($this->once()) + ->method('buildView') + ->with($this->anything(), $form, $options) + ->will($this->returnCallback($assertIndexAndNbOfChildViews(2, 0))); + + $extension2->expects($this->once()) + ->method('buildView') + ->with($this->anything(), $form, $options) + ->will($this->returnCallback($assertIndexAndNbOfChildViews(3, 0))); + + // Now the first child form + $field1Type->expects($this->once()) + ->method('buildView') + ->will($this->returnCallback($assertIndexAndNbOfChildViews(4, 0))); + $field1Type->expects($this->once()) + ->method('finishView') + ->will($this->returnCallback($assertIndexAndNbOfChildViews(5, 0))); + + // And the second child form + $field2Type->expects($this->once()) + ->method('buildView') + ->will($this->returnCallback($assertIndexAndNbOfChildViews(6, 0))); + $field2Type->expects($this->once()) + ->method('finishView') + ->will($this->returnCallback($assertIndexAndNbOfChildViews(7, 0))); + + // Again first the parent + $parentType->expects($this->once()) + ->method('finishView') + ->with($this->anything(), $form, $options) + ->will($this->returnCallback($assertIndexAndNbOfChildViews(8, 2))); + + // Then the type itself + $type->expects($this->once()) + ->method('finishView') + ->with($this->anything(), $form, $options) + ->will($this->returnCallback($assertIndexAndNbOfChildViews(9, 2))); + + // Then its extensions + $extension1->expects($this->once()) + ->method('finishView') + ->with($this->anything(), $form, $options) + ->will($this->returnCallback($assertIndexAndNbOfChildViews(10, 2))); + + $extension2->expects($this->once()) + ->method('finishView') + ->with($this->anything(), $form, $options) + ->will($this->returnCallback($assertIndexAndNbOfChildViews(11, 2))); + + $parentView = new FormView(); + $view = $resolvedType->createView($form, $parentView); + + $this->assertSame($parentView, $view->parent); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getMockFormType() + { + return $this->getMock('Symfony\Component\Form\FormTypeInterface'); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getMockFormTypeExtension() + { + return $this->getMock('Symfony\Component\Form\FormTypeExtensionInterface'); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getMockFormFactory() + { + return $this->getMock('Symfony\Component\Form\FormFactoryInterface'); + } + + /** + * @param string $name + * @param array $options + * + * @return FormBuilder + */ + protected function getBuilder($name = 'name', array $options = array()) + { + return new FormBuilder($name, null, $this->dispatcher, $this->factory, $options); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Tests/SimpleFormTest.php b/vendor/symfony/form/Symfony/Component/Form/Tests/SimpleFormTest.php new file mode 100644 index 00000000..bedad676 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Tests/SimpleFormTest.php @@ -0,0 +1,1045 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\Form; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\FormEvents; +use Symfony\Component\PropertyAccess\PropertyPath; +use Symfony\Component\Form\FormConfigBuilder; +use Symfony\Component\Form\FormError; +use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\Form\Tests\Fixtures\FixedDataTransformer; +use Symfony\Component\Form\Tests\Fixtures\FixedFilterListener; + +class SimpleFormTest_Countable implements \Countable +{ + private $count; + + public function __construct($count) + { + $this->count = $count; + } + + public function count() + { + return $this->count; + } +} + +class SimpleFormTest_Traversable implements \IteratorAggregate +{ + private $iterator; + + public function __construct($count) + { + $this->iterator = new \ArrayIterator($count > 0 ? array_fill(0, $count, 'Foo') : array()); + } + + public function getIterator() + { + return $this->iterator; + } +} + +class SimpleFormTest extends AbstractFormTest +{ + public function testDataIsInitializedToConfiguredValue() + { + $model = new FixedDataTransformer(array( + 'default' => 'foo', + )); + $view = new FixedDataTransformer(array( + 'foo' => 'bar', + )); + + $config = new FormConfigBuilder('name', null, $this->dispatcher); + $config->addViewTransformer($view); + $config->addModelTransformer($model); + $config->setData('default'); + $form = new Form($config); + + $this->assertSame('default', $form->getData()); + $this->assertSame('foo', $form->getNormData()); + $this->assertSame('bar', $form->getViewData()); + } + + // https://github.com/symfony/symfony/commit/d4f4038f6daf7cf88ca7c7ab089473cce5ebf7d8#commitcomment-1632879 + public function testDataIsInitializedFromSubmit() + { + $mock = $this->getMockBuilder('\stdClass') + ->setMethods(array('preSetData', 'preSubmit')) + ->getMock(); + $mock->expects($this->at(0)) + ->method('preSetData'); + $mock->expects($this->at(1)) + ->method('preSubmit'); + + $config = new FormConfigBuilder('name', null, $this->dispatcher); + $config->addEventListener(FormEvents::PRE_SET_DATA, array($mock, 'preSetData')); + $config->addEventListener(FormEvents::PRE_SUBMIT, array($mock, 'preSubmit')); + $form = new Form($config); + + // no call to setData() or similar where the object would be + // initialized otherwise + + $form->submit('foobar'); + } + + // https://github.com/symfony/symfony/pull/7789 + public function testFalseIsConvertedToNull() + { + $mock = $this->getMockBuilder('\stdClass') + ->setMethods(array('preBind')) + ->getMock(); + $mock->expects($this->once()) + ->method('preBind') + ->with($this->callback(function ($event) { + return null === $event->getData(); + })); + + $config = new FormConfigBuilder('name', null, $this->dispatcher); + $config->addEventListener(FormEvents::PRE_BIND, array($mock, 'preBind')); + $form = new Form($config); + + $form->bind(false); + + $this->assertTrue($form->isValid()); + $this->assertNull($form->getData()); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\AlreadySubmittedException + */ + public function testSubmitThrowsExceptionIfAlreadySubmitted() + { + $this->form->submit(array()); + $this->form->submit(array()); + } + + public function testSubmitIsIgnoredIfDisabled() + { + $form = $this->getBuilder() + ->setDisabled(true) + ->setData('initial') + ->getForm(); + + $form->submit('new'); + + $this->assertEquals('initial', $form->getData()); + $this->assertTrue($form->isSubmitted()); + } + + public function testNeverRequiredIfParentNotRequired() + { + $parent = $this->getBuilder()->setRequired(false)->getForm(); + $child = $this->getBuilder()->setRequired(true)->getForm(); + + $child->setParent($parent); + + $this->assertFalse($child->isRequired()); + } + + public function testRequired() + { + $parent = $this->getBuilder()->setRequired(true)->getForm(); + $child = $this->getBuilder()->setRequired(true)->getForm(); + + $child->setParent($parent); + + $this->assertTrue($child->isRequired()); + } + + public function testNotRequired() + { + $parent = $this->getBuilder()->setRequired(true)->getForm(); + $child = $this->getBuilder()->setRequired(false)->getForm(); + + $child->setParent($parent); + + $this->assertFalse($child->isRequired()); + } + + public function testAlwaysDisabledIfParentDisabled() + { + $parent = $this->getBuilder()->setDisabled(true)->getForm(); + $child = $this->getBuilder()->setDisabled(false)->getForm(); + + $child->setParent($parent); + + $this->assertTrue($child->isDisabled()); + } + + public function testDisabled() + { + $parent = $this->getBuilder()->setDisabled(false)->getForm(); + $child = $this->getBuilder()->setDisabled(true)->getForm(); + + $child->setParent($parent); + + $this->assertTrue($child->isDisabled()); + } + + public function testNotDisabled() + { + $parent = $this->getBuilder()->setDisabled(false)->getForm(); + $child = $this->getBuilder()->setDisabled(false)->getForm(); + + $child->setParent($parent); + + $this->assertFalse($child->isDisabled()); + } + + public function testGetRootReturnsRootOfParent() + { + $parent = $this->getMockForm(); + $parent->expects($this->once()) + ->method('getRoot') + ->will($this->returnValue('ROOT')); + + $this->form->setParent($parent); + + $this->assertEquals('ROOT', $this->form->getRoot()); + } + + public function testGetRootReturnsSelfIfNoParent() + { + $this->assertSame($this->form, $this->form->getRoot()); + } + + public function testEmptyIfEmptyArray() + { + $this->form->setData(array()); + + $this->assertTrue($this->form->isEmpty()); + } + + public function testEmptyIfEmptyCountable() + { + $this->form = new Form(new FormConfigBuilder('name', __NAMESPACE__.'\SimpleFormTest_Countable', $this->dispatcher)); + + $this->form->setData(new SimpleFormTest_Countable(0)); + + $this->assertTrue($this->form->isEmpty()); + } + + public function testNotEmptyIfFilledCountable() + { + $this->form = new Form(new FormConfigBuilder('name', __NAMESPACE__.'\SimpleFormTest_Countable', $this->dispatcher)); + + $this->form->setData(new SimpleFormTest_Countable(1)); + + $this->assertFalse($this->form->isEmpty()); + } + + public function testEmptyIfEmptyTraversable() + { + $this->form = new Form(new FormConfigBuilder('name', __NAMESPACE__.'\SimpleFormTest_Traversable', $this->dispatcher)); + + $this->form->setData(new SimpleFormTest_Traversable(0)); + + $this->assertTrue($this->form->isEmpty()); + } + + public function testNotEmptyIfFilledTraversable() + { + $this->form = new Form(new FormConfigBuilder('name', __NAMESPACE__.'\SimpleFormTest_Traversable', $this->dispatcher)); + + $this->form->setData(new SimpleFormTest_Traversable(1)); + + $this->assertFalse($this->form->isEmpty()); + } + + public function testEmptyIfNull() + { + $this->form->setData(null); + + $this->assertTrue($this->form->isEmpty()); + } + + public function testEmptyIfEmptyString() + { + $this->form->setData(''); + + $this->assertTrue($this->form->isEmpty()); + } + + public function testNotEmptyIfText() + { + $this->form->setData('foobar'); + + $this->assertFalse($this->form->isEmpty()); + } + + public function testValidIfSubmitted() + { + $form = $this->getBuilder()->getForm(); + $form->submit('foobar'); + + $this->assertTrue($form->isValid()); + } + + public function testValidIfSubmittedAndDisabled() + { + $form = $this->getBuilder()->setDisabled(true)->getForm(); + $form->submit('foobar'); + + $this->assertTrue($form->isValid()); + } + + public function testNotValidIfNotSubmitted() + { + $this->assertFalse($this->form->isValid()); + } + + public function testNotValidIfErrors() + { + $form = $this->getBuilder()->getForm(); + $form->submit('foobar'); + $form->addError(new FormError('Error!')); + + $this->assertFalse($form->isValid()); + } + + public function testHasErrors() + { + $this->form->addError(new FormError('Error!')); + + $this->assertCount(1, $this->form->getErrors()); + } + + public function testHasNoErrors() + { + $this->assertCount(0, $this->form->getErrors()); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\AlreadySubmittedException + */ + public function testSetParentThrowsExceptionIfAlreadySubmitted() + { + $this->form->submit(array()); + $this->form->setParent($this->getBuilder('parent')->getForm()); + } + + public function testSubmitted() + { + $form = $this->getBuilder()->getForm(); + $form->submit('foobar'); + + $this->assertTrue($form->isSubmitted()); + } + + public function testNotSubmitted() + { + $this->assertFalse($this->form->isSubmitted()); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\AlreadySubmittedException + */ + public function testSetDataThrowsExceptionIfAlreadySubmitted() + { + $this->form->submit(array()); + $this->form->setData(null); + } + + public function testSetDataClonesObjectIfNotByReference() + { + $data = new \stdClass(); + $form = $this->getBuilder('name', null, '\stdClass')->setByReference(false)->getForm(); + $form->setData($data); + + $this->assertNotSame($data, $form->getData()); + $this->assertEquals($data, $form->getData()); + } + + public function testSetDataDoesNotCloneObjectIfByReference() + { + $data = new \stdClass(); + $form = $this->getBuilder('name', null, '\stdClass')->setByReference(true)->getForm(); + $form->setData($data); + + $this->assertSame($data, $form->getData()); + } + + public function testSetDataExecutesTransformationChain() + { + // use real event dispatcher now + $form = $this->getBuilder('name', new EventDispatcher()) + ->addEventSubscriber(new FixedFilterListener(array( + 'preSetData' => array( + 'app' => 'filtered', + ), + ))) + ->addModelTransformer(new FixedDataTransformer(array( + '' => '', + 'filtered' => 'norm', + ))) + ->addViewTransformer(new FixedDataTransformer(array( + '' => '', + 'norm' => 'client', + ))) + ->getForm(); + + $form->setData('app'); + + $this->assertEquals('filtered', $form->getData()); + $this->assertEquals('norm', $form->getNormData()); + $this->assertEquals('client', $form->getViewData()); + } + + public function testSetDataExecutesViewTransformersInOrder() + { + $form = $this->getBuilder() + ->addViewTransformer(new FixedDataTransformer(array( + '' => '', + 'first' => 'second', + ))) + ->addViewTransformer(new FixedDataTransformer(array( + '' => '', + 'second' => 'third', + ))) + ->getForm(); + + $form->setData('first'); + + $this->assertEquals('third', $form->getViewData()); + } + + public function testSetDataExecutesModelTransformersInReverseOrder() + { + $form = $this->getBuilder() + ->addModelTransformer(new FixedDataTransformer(array( + '' => '', + 'second' => 'third', + ))) + ->addModelTransformer(new FixedDataTransformer(array( + '' => '', + 'first' => 'second', + ))) + ->getForm(); + + $form->setData('first'); + + $this->assertEquals('third', $form->getNormData()); + } + + /* + * When there is no data transformer, the data must have the same format + * in all three representations + */ + public function testSetDataConvertsScalarToStringIfNoTransformer() + { + $form = $this->getBuilder()->getForm(); + + $form->setData(1); + + $this->assertSame('1', $form->getData()); + $this->assertSame('1', $form->getNormData()); + $this->assertSame('1', $form->getViewData()); + } + + /* + * Data in client format should, if possible, always be a string to + * facilitate differentiation between '0' and '' + */ + public function testSetDataConvertsScalarToStringIfOnlyModelTransformer() + { + $form = $this->getBuilder() + ->addModelTransformer(new FixedDataTransformer(array( + '' => '', + 1 => 23, + ))) + ->getForm(); + + $form->setData(1); + + $this->assertSame(1, $form->getData()); + $this->assertSame(23, $form->getNormData()); + $this->assertSame('23', $form->getViewData()); + } + + /* + * NULL remains NULL in app and norm format to remove the need to treat + * empty values and NULL explicitly in the application + */ + public function testSetDataConvertsNullToStringIfNoTransformer() + { + $form = $this->getBuilder()->getForm(); + + $form->setData(null); + + $this->assertNull($form->getData()); + $this->assertNull($form->getNormData()); + $this->assertSame('', $form->getViewData()); + } + + public function testSetDataIsIgnoredIfDataIsLocked() + { + $form = $this->getBuilder() + ->setData('default') + ->setDataLocked(true) + ->getForm(); + + $form->setData('foobar'); + + $this->assertSame('default', $form->getData()); + } + + public function testSubmitConvertsEmptyToNullIfNoTransformer() + { + $form = $this->getBuilder()->getForm(); + + $form->submit(''); + + $this->assertNull($form->getData()); + $this->assertNull($form->getNormData()); + $this->assertSame('', $form->getViewData()); + } + + public function testSubmitExecutesTransformationChain() + { + // use real event dispatcher now + $form = $this->getBuilder('name', new EventDispatcher()) + ->addEventSubscriber(new FixedFilterListener(array( + 'preSubmit' => array( + 'client' => 'filteredclient', + ), + 'onSubmit' => array( + 'norm' => 'filterednorm', + ), + ))) + ->addViewTransformer(new FixedDataTransformer(array( + '' => '', + // direction is reversed! + 'norm' => 'filteredclient', + 'filterednorm' => 'cleanedclient' + ))) + ->addModelTransformer(new FixedDataTransformer(array( + '' => '', + // direction is reversed! + 'app' => 'filterednorm', + ))) + ->getForm(); + + $form->submit('client'); + + $this->assertEquals('app', $form->getData()); + $this->assertEquals('filterednorm', $form->getNormData()); + $this->assertEquals('cleanedclient', $form->getViewData()); + } + + public function testSubmitExecutesViewTransformersInReverseOrder() + { + $form = $this->getBuilder() + ->addViewTransformer(new FixedDataTransformer(array( + '' => '', + 'third' => 'second', + ))) + ->addViewTransformer(new FixedDataTransformer(array( + '' => '', + 'second' => 'first', + ))) + ->getForm(); + + $form->submit('first'); + + $this->assertEquals('third', $form->getNormData()); + } + + public function testSubmitExecutesModelTransformersInOrder() + { + $form = $this->getBuilder() + ->addModelTransformer(new FixedDataTransformer(array( + '' => '', + 'second' => 'first', + ))) + ->addModelTransformer(new FixedDataTransformer(array( + '' => '', + 'third' => 'second', + ))) + ->getForm(); + + $form->submit('first'); + + $this->assertEquals('third', $form->getData()); + } + + public function testSynchronizedByDefault() + { + $this->assertTrue($this->form->isSynchronized()); + } + + public function testSynchronizedAfterSubmission() + { + $this->form->submit('foobar'); + + $this->assertTrue($this->form->isSynchronized()); + } + + public function testNotSynchronizedIfViewReverseTransformationFailed() + { + $transformer = $this->getDataTransformer(); + $transformer->expects($this->once()) + ->method('reverseTransform') + ->will($this->throwException(new TransformationFailedException())); + + $form = $this->getBuilder() + ->addViewTransformer($transformer) + ->getForm(); + + $form->submit('foobar'); + + $this->assertFalse($form->isSynchronized()); + } + + public function testNotSynchronizedIfModelReverseTransformationFailed() + { + $transformer = $this->getDataTransformer(); + $transformer->expects($this->once()) + ->method('reverseTransform') + ->will($this->throwException(new TransformationFailedException())); + + $form = $this->getBuilder() + ->addModelTransformer($transformer) + ->getForm(); + + $form->submit('foobar'); + + $this->assertFalse($form->isSynchronized()); + } + + public function testEmptyDataCreatedBeforeTransforming() + { + $form = $this->getBuilder() + ->setEmptyData('foo') + ->addViewTransformer(new FixedDataTransformer(array( + '' => '', + // direction is reversed! + 'bar' => 'foo', + ))) + ->getForm(); + + $form->submit(''); + + $this->assertEquals('bar', $form->getData()); + } + + public function testEmptyDataFromClosure() + { + $test = $this; + $form = $this->getBuilder() + ->setEmptyData(function ($form) use ($test) { + // the form instance is passed to the closure to allow use + // of form data when creating the empty value + $test->assertInstanceOf('Symfony\Component\Form\FormInterface', $form); + + return 'foo'; + }) + ->addViewTransformer(new FixedDataTransformer(array( + '' => '', + // direction is reversed! + 'bar' => 'foo', + ))) + ->getForm(); + + $form->submit(''); + + $this->assertEquals('bar', $form->getData()); + } + + public function testSubmitResetsErrors() + { + $this->form->addError(new FormError('Error!')); + $this->form->submit('foobar'); + + $this->assertSame(array(), $this->form->getErrors()); + } + + public function testCreateView() + { + $type = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + $view = $this->getMock('Symfony\Component\Form\FormView'); + $form = $this->getBuilder()->setType($type)->getForm(); + + $type->expects($this->once()) + ->method('createView') + ->with($form) + ->will($this->returnValue($view)); + + $this->assertSame($view, $form->createView()); + } + + public function testCreateViewWithParent() + { + $type = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + $view = $this->getMock('Symfony\Component\Form\FormView'); + $parentForm = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $parentView = $this->getMock('Symfony\Component\Form\FormView'); + $form = $this->getBuilder()->setType($type)->getForm(); + $form->setParent($parentForm); + + $parentForm->expects($this->once()) + ->method('createView') + ->will($this->returnValue($parentView)); + + $type->expects($this->once()) + ->method('createView') + ->with($form, $parentView) + ->will($this->returnValue($view)); + + $this->assertSame($view, $form->createView()); + } + + public function testCreateViewWithExplicitParent() + { + $type = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + $view = $this->getMock('Symfony\Component\Form\FormView'); + $parentView = $this->getMock('Symfony\Component\Form\FormView'); + $form = $this->getBuilder()->setType($type)->getForm(); + + $type->expects($this->once()) + ->method('createView') + ->with($form, $parentView) + ->will($this->returnValue($view)); + + $this->assertSame($view, $form->createView($parentView)); + } + + public function testGetErrorsAsString() + { + $this->form->addError(new FormError('Error!')); + + $this->assertEquals("ERROR: Error!\n", $this->form->getErrorsAsString()); + } + + public function testFormCanHaveEmptyName() + { + $form = $this->getBuilder('')->getForm(); + + $this->assertEquals('', $form->getName()); + } + + public function testSetNullParentWorksWithEmptyName() + { + $form = $this->getBuilder('')->getForm(); + $form->setParent(null); + + $this->assertNull($form->getParent()); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\LogicException + * @expectedExceptionMessage A form with an empty name cannot have a parent form. + */ + public function testFormCannotHaveEmptyNameNotInRootLevel() + { + $this->getBuilder() + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->add($this->getBuilder('')) + ->getForm(); + } + + public function testGetPropertyPathReturnsConfiguredPath() + { + $form = $this->getBuilder()->setPropertyPath('address.street')->getForm(); + + $this->assertEquals(new PropertyPath('address.street'), $form->getPropertyPath()); + } + + // see https://github.com/symfony/symfony/issues/3903 + public function testGetPropertyPathDefaultsToNameIfParentHasDataClass() + { + $parent = $this->getBuilder(null, null, 'stdClass') + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $form = $this->getBuilder('name')->getForm(); + $parent->add($form); + + $this->assertEquals(new PropertyPath('name'), $form->getPropertyPath()); + } + + // see https://github.com/symfony/symfony/issues/3903 + public function testGetPropertyPathDefaultsToIndexedNameIfParentDataClassIsNull() + { + $parent = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $form = $this->getBuilder('name')->getForm(); + $parent->add($form); + + $this->assertEquals(new PropertyPath('[name]'), $form->getPropertyPath()); + } + + public function testGetPropertyPathDefaultsToNameIfFirstParentWithoutInheritDataHasDataClass() + { + $grandParent = $this->getBuilder(null, null, 'stdClass') + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $parent = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->setInheritData(true) + ->getForm(); + $form = $this->getBuilder('name')->getForm(); + $grandParent->add($parent); + $parent->add($form); + + $this->assertEquals(new PropertyPath('name'), $form->getPropertyPath()); + } + + public function testGetPropertyPathDefaultsToIndexedNameIfDataClassOfFirstParentWithoutInheritDataIsNull() + { + $grandParent = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $parent = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->setInheritData(true) + ->getForm(); + $form = $this->getBuilder('name')->getForm(); + $grandParent->add($parent); + $parent->add($form); + + $this->assertEquals(new PropertyPath('[name]'), $form->getPropertyPath()); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\LogicException + */ + public function testViewDataMustNotBeObjectIfDataClassIsNull() + { + $config = new FormConfigBuilder('name', null, $this->dispatcher); + $config->addViewTransformer(new FixedDataTransformer(array( + '' => '', + 'foo' => new \stdClass(), + ))); + $form = new Form($config); + + $form->setData('foo'); + } + + public function testViewDataMayBeArrayAccessIfDataClassIsNull() + { + $arrayAccess = $this->getMock('\ArrayAccess'); + $config = new FormConfigBuilder('name', null, $this->dispatcher); + $config->addViewTransformer(new FixedDataTransformer(array( + '' => '', + 'foo' => $arrayAccess, + ))); + $form = new Form($config); + + $form->setData('foo'); + + $this->assertSame($arrayAccess, $form->getViewData()); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\LogicException + */ + public function testViewDataMustBeObjectIfDataClassIsSet() + { + $config = new FormConfigBuilder('name', 'stdClass', $this->dispatcher); + $config->addViewTransformer(new FixedDataTransformer(array( + '' => '', + 'foo' => array('bar' => 'baz'), + ))); + $form = new Form($config); + + $form->setData('foo'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\RuntimeException + */ + public function testSetDataCannotInvokeItself() + { + // Cycle detection to prevent endless loops + $config = new FormConfigBuilder('name', 'stdClass', $this->dispatcher); + $config->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { + $event->getForm()->setData('bar'); + }); + $form = new Form($config); + + $form->setData('foo'); + } + + public function testSubmittingWrongDataIsIgnored() + { + $test = $this; + + $child = $this->getBuilder('child', $this->dispatcher); + $child->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($test) { + // child form doesn't receive the wrong data that is submitted on parent + $test->assertNull($event->getData()); + }); + + $parent = $this->getBuilder('parent', new EventDispatcher()) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->add($child) + ->getForm(); + + $parent->submit('not-an-array'); + } + + public function testHandleRequestForwardsToRequestHandler() + { + $handler = $this->getMock('Symfony\Component\Form\RequestHandlerInterface'); + + $form = $this->getBuilder() + ->setRequestHandler($handler) + ->getForm(); + + $handler->expects($this->once()) + ->method('handleRequest') + ->with($this->identicalTo($form), 'REQUEST'); + + $this->assertSame($form, $form->handleRequest('REQUEST')); + } + + public function testFormInheritsParentData() + { + $child = $this->getBuilder('child') + ->setInheritData(true); + + $parent = $this->getBuilder('parent') + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->setData('foo') + ->addModelTransformer(new FixedDataTransformer(array( + 'foo' => 'norm[foo]', + ))) + ->addViewTransformer(new FixedDataTransformer(array( + 'norm[foo]' => 'view[foo]', + ))) + ->add($child) + ->getForm(); + + $this->assertSame('foo', $parent->get('child')->getData()); + $this->assertSame('norm[foo]', $parent->get('child')->getNormData()); + $this->assertSame('view[foo]', $parent->get('child')->getViewData()); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\RuntimeException + */ + public function testInheritDataDisallowsSetData() + { + $form = $this->getBuilder() + ->setInheritData(true) + ->getForm(); + + $form->setData('foo'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\RuntimeException + */ + public function testGetDataRequiresParentToBeSetIfInheritData() + { + $form = $this->getBuilder() + ->setInheritData(true) + ->getForm(); + + $form->getData(); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\RuntimeException + */ + public function testGetNormDataRequiresParentToBeSetIfInheritData() + { + $form = $this->getBuilder() + ->setInheritData(true) + ->getForm(); + + $form->getNormData(); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\RuntimeException + */ + public function testGetViewDataRequiresParentToBeSetIfInheritData() + { + $form = $this->getBuilder() + ->setInheritData(true) + ->getForm(); + + $form->getViewData(); + } + + public function testPostSubmitDataIsNullIfInheritData() + { + $test = $this; + $form = $this->getBuilder() + ->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) use ($test) { + $test->assertNull($event->getData()); + }) + ->setInheritData(true) + ->getForm(); + + $form->submit('foo'); + } + + public function testSubmitIsNeverFiredIfInheritData() + { + $test = $this; + $form = $this->getBuilder() + ->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) use ($test) { + $test->fail('The SUBMIT event should not be fired'); + }) + ->setInheritData(true) + ->getForm(); + + $form->submit('foo'); + } + + public function testInitializeSetsDefaultData() + { + $config = $this->getBuilder()->setData('DEFAULT')->getFormConfig(); + $form = $this->getMock('Symfony\Component\Form\Form', array('setData'), array($config)); + + $form->expects($this->once()) + ->method('setData') + ->with($this->identicalTo('DEFAULT')); + + /* @var Form $form */ + $form->initialize(); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\RuntimeException + */ + public function testInitializeFailsIfParent() + { + $parent = $this->getBuilder()->setRequired(false)->getForm(); + $child = $this->getBuilder()->setRequired(true)->getForm(); + + $child->setParent($parent); + + $child->initialize(); + } + + protected function createForm() + { + return $this->getBuilder()->getForm(); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Util/FormUtil.php b/vendor/symfony/form/Symfony/Component/Form/Util/FormUtil.php new file mode 100644 index 00000000..7647691e --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Util/FormUtil.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Util; + +/** + * @author Bernhard Schussek + */ +class FormUtil +{ + /** + * This class should not be instantiated + */ + private function __construct() {} + + /** + * Returns whether the given data is empty. + * + * This logic is reused multiple times throughout the processing of + * a form and needs to be consistent. PHP's keyword `empty` cannot + * be used as it also considers 0 and "0" to be empty. + * + * @param mixed $data + * + * @return Boolean + */ + public static function isEmpty($data) + { + // Should not do a check for array() === $data!!! + // This method is used in occurrences where arrays are + // not considered to be empty, ever. + return null === $data || '' === $data; + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Util/InheritDataAwareIterator.php b/vendor/symfony/form/Symfony/Component/Form/Util/InheritDataAwareIterator.php new file mode 100644 index 00000000..5c2c5fad --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Util/InheritDataAwareIterator.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Util; + +/** + * Iterator that returns only forms from a form tree that do not inherit their + * parent data. + * + * If the iterator encounters a form that inherits its parent data, it enters + * the form and traverses its children as well. + * + * @author Bernhard Schussek + */ +class InheritDataAwareIterator extends VirtualFormAwareIterator +{ + /** + * Creates a new iterator. + * + * @param \Symfony\Component\Form\FormInterface[] $forms An array + */ + public function __construct(array $forms) + { + // Skip the deprecation error + \ArrayIterator::__construct($forms); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/Util/VirtualFormAwareIterator.php b/vendor/symfony/form/Symfony/Component/Form/Util/VirtualFormAwareIterator.php new file mode 100644 index 00000000..24fdc8bb --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Util/VirtualFormAwareIterator.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Util; + +/** + * Iterator that returns only forms from a form tree that do not inherit their + * parent data. + * + * If the iterator encounters a form that inherits its parent data, it enters + * the form and traverses its children as well. + * + * @author Bernhard Schussek + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link InheritDataAwareIterator} instead. + */ +class VirtualFormAwareIterator extends \ArrayIterator implements \RecursiveIterator +{ + /** + * Creates a new iterator. + * + * @param \Symfony\Component\Form\FormInterface[] $forms An array + */ + public function __construct(array $forms) + { + // Uncomment this as soon as the deprecation note should be shown + // trigger_error('VirtualFormAwareIterator is deprecated since version 2.3 and will be removed in 3.0. Use InheritDataAwareIterator instead.', E_USER_DEPRECATED); + + parent::__construct($forms); + } + + public function getChildren() + { + return new static($this->current()->all()); + } + + public function hasChildren() + { + return $this->current()->getConfig()->getInheritData(); + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/composer.json b/vendor/symfony/form/Symfony/Component/Form/composer.json new file mode 100644 index 00000000..73415011 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/composer.json @@ -0,0 +1,43 @@ +{ + "name": "symfony/form", + "type": "library", + "description": "Symfony Form Component", + "keywords": [], + "homepage": "http://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3", + "symfony/event-dispatcher": "~2.1", + "symfony/intl": "~2.3", + "symfony/options-resolver": "~2.1", + "symfony/property-access": "~2.2" + }, + "require-dev": { + "symfony/validator": "~2.2", + "symfony/http-foundation": "~2.2" + }, + "suggest": { + "symfony/validator": "", + "symfony/http-foundation": "" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\Form\\": "" } + }, + "target-dir": "Symfony/Component/Form", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + } +} diff --git a/vendor/symfony/form/Symfony/Component/Form/phpunit.xml.dist b/vendor/symfony/form/Symfony/Component/Form/phpunit.xml.dist new file mode 100644 index 00000000..d0d261f1 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/icu/Symfony/Component/Icu/.gitignore b/vendor/symfony/icu/Symfony/Component/Icu/.gitignore new file mode 100644 index 00000000..c49a5d8d --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/icu/Symfony/Component/Icu/IcuCurrencyBundle.php b/vendor/symfony/icu/Symfony/Component/Icu/IcuCurrencyBundle.php new file mode 100644 index 00000000..7c47e4e3 --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/IcuCurrencyBundle.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Icu; + +use Symfony\Component\Intl\ResourceBundle\CurrencyBundle; +use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface; + +/** + * A stub implementation of {@link \Symfony\Component\Intl\ResourceBundle\CurrencyBundleInterface}. + * + * @author Bernhard Schussek + */ +class IcuCurrencyBundle extends CurrencyBundle +{ + public function __construct(StructuredBundleReaderInterface $reader) + { + parent::__construct(realpath(IcuData::getResourceDirectory() . '/curr'), $reader); + } +} diff --git a/vendor/symfony/icu/Symfony/Component/Icu/IcuData.php b/vendor/symfony/icu/Symfony/Component/Icu/IcuData.php new file mode 100644 index 00000000..478a2f0e --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/IcuData.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Icu; + +use Symfony\Component\Intl\ResourceBundle\Reader\PhpBundleReader; + +/** + * @author Bernhard Schussek + */ +class IcuData +{ + /** + * Returns the version of the bundled ICU data. + * + * @return string The version string. + */ + public static function getVersion() + { + return trim(file_get_contents(__DIR__ . '/Resources/data/version.txt')); + } + + /** + * Returns whether the ICU data is stubbed. + * + * @return Boolean Returns true if the ICU data is stubbed, false if it is + * loaded from ICU .res files. + */ + public static function isStubbed() + { + return true; + } + + /** + * Returns the path to the directory where the resource bundles are stored. + * + * @return string The absolute path to the resource directory. + */ + public static function getResourceDirectory() + { + return realpath(__DIR__ . '/Resources/data'); + } + + /** + * Returns a reader for reading resource bundles in this component. + * + * @return \Symfony\Component\Intl\ResourceBundle\Reader\BundleReaderInterface + */ + public static function getBundleReader() + { + return new PhpBundleReader(); + } + + /** + * This class must not be instantiated. + */ + private function __construct() {} +} diff --git a/vendor/symfony/icu/Symfony/Component/Icu/IcuLanguageBundle.php b/vendor/symfony/icu/Symfony/Component/Icu/IcuLanguageBundle.php new file mode 100644 index 00000000..a3de4316 --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/IcuLanguageBundle.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Icu; + +use Symfony\Component\Intl\ResourceBundle\LanguageBundle; +use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface; + +/** + * A stub implementation of {@link \Symfony\Component\Intl\ResourceBundle\LanguageBundleInterface}. + * + * @author Bernhard Schussek + */ +class IcuLanguageBundle extends LanguageBundle +{ + public function __construct(StructuredBundleReaderInterface $reader) + { + parent::__construct(realpath(IcuData::getResourceDirectory() . '/lang'), $reader); + } +} diff --git a/vendor/symfony/icu/Symfony/Component/Icu/IcuLocaleBundle.php b/vendor/symfony/icu/Symfony/Component/Icu/IcuLocaleBundle.php new file mode 100644 index 00000000..a9a54384 --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/IcuLocaleBundle.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Icu; + +use Symfony\Component\Intl\ResourceBundle\LocaleBundle; +use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface; + +/** + * A stub implementation of {@link \Symfony\Component\Intl\ResourceBundle\LocaleBundleInterface}. + * + * @author Bernhard Schussek + */ +class IcuLocaleBundle extends LocaleBundle +{ + public function __construct(StructuredBundleReaderInterface $reader) + { + parent::__construct(realpath(IcuData::getResourceDirectory() . '/locales'), $reader); + } +} diff --git a/vendor/symfony/icu/Symfony/Component/Icu/IcuRegionBundle.php b/vendor/symfony/icu/Symfony/Component/Icu/IcuRegionBundle.php new file mode 100644 index 00000000..639a4339 --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/IcuRegionBundle.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Icu; + +use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface; +use Symfony\Component\Intl\ResourceBundle\RegionBundle; + +/** + * A stub implementation of {@link \Symfony\Component\Intl\ResourceBundle\RegionBundleInterface}. + * + * @author Bernhard Schussek + */ +class IcuRegionBundle extends RegionBundle +{ + public function __construct(StructuredBundleReaderInterface $reader) + { + parent::__construct(realpath(IcuData::getResourceDirectory() . '/region'), $reader); + } +} diff --git a/vendor/symfony/icu/Symfony/Component/Icu/LICENSE b/vendor/symfony/icu/Symfony/Component/Icu/LICENSE new file mode 100644 index 00000000..88a57f8d --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2013 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. diff --git a/vendor/symfony/icu/Symfony/Component/Icu/README.md b/vendor/symfony/icu/Symfony/Component/Icu/README.md new file mode 100644 index 00000000..b0a16edd --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/README.md @@ -0,0 +1,18 @@ +Icu Component +============= + +Contains data of the ICU library in a specific version. + +You should not directly use this component. Use it through the API of the +[Intl component] [1] instead. + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/Icu/ + $ composer.phar install --dev + $ phpunit + +[1]: https://github.com/symfony/Intl diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en.php b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en.php new file mode 100644 index 00000000..3b38213c --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en.php @@ -0,0 +1,1791 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'Currencies' => array( + 'XUA' => array( + 0 => 'ADB Unit of Account', + 1 => 'XUA', + 2 => 2, + 3 => 0, + ), + 'AFN' => array( + 0 => 'Afghan Afghani', + 1 => 'AFN', + 2 => 0, + 3 => 0, + ), + 'AFA' => array( + 0 => 'Afghan Afghani (1927-2002)', + 1 => 'AFA', + 2 => 2, + 3 => 0, + ), + 'ALL' => array( + 0 => 'Albanian Lek', + 1 => 'ALL', + 2 => 0, + 3 => 0, + ), + 'ALK' => array( + 0 => 'Albanian Lek (1946-1965)', + 1 => 'ALK', + 2 => 2, + 3 => 0, + ), + 'DZD' => array( + 0 => 'Algerian Dinar', + 1 => 'DZD', + 2 => 2, + 3 => 0, + ), + 'ADP' => array( + 0 => 'Andorran Peseta', + 1 => 'ADP', + 2 => 0, + 3 => 0, + ), + 'AOA' => array( + 0 => 'Angolan Kwanza', + 1 => 'AOA', + 2 => 2, + 3 => 0, + ), + 'AOK' => array( + 0 => 'Angolan Kwanza (1977-1991)', + 1 => 'AOK', + 2 => 2, + 3 => 0, + ), + 'AON' => array( + 0 => 'Angolan New Kwanza (1990-2000)', + 1 => 'AON', + 2 => 2, + 3 => 0, + ), + 'AOR' => array( + 0 => 'Angolan Readjusted Kwanza (1995-1999)', + 1 => 'AOR', + 2 => 2, + 3 => 0, + ), + 'ARA' => array( + 0 => 'Argentine Austral', + 1 => 'ARA', + 2 => 2, + 3 => 0, + ), + 'ARS' => array( + 0 => 'Argentine Peso', + 1 => 'ARS', + 2 => 2, + 3 => 0, + ), + 'ARM' => array( + 0 => 'Argentine Peso (1881-1970)', + 1 => 'ARM', + 2 => 2, + 3 => 0, + ), + 'ARP' => array( + 0 => 'Argentine Peso (1983-1985)', + 1 => 'ARP', + 2 => 2, + 3 => 0, + ), + 'ARL' => array( + 0 => 'Argentine Peso Ley (1970-1983)', + 1 => 'ARL', + 2 => 2, + 3 => 0, + ), + 'AMD' => array( + 0 => 'Armenian Dram', + 1 => 'AMD', + 2 => 0, + 3 => 0, + ), + 'AWG' => array( + 0 => 'Aruban Florin', + 1 => 'AWG', + 2 => 2, + 3 => 0, + ), + 'AUD' => array( + 0 => 'Australian Dollar', + 1 => 'A$', + 2 => 2, + 3 => 0, + ), + 'ATS' => array( + 0 => 'Austrian Schilling', + 1 => 'ATS', + 2 => 2, + 3 => 0, + ), + 'AZN' => array( + 0 => 'Azerbaijani Manat', + 1 => 'AZN', + 2 => 2, + 3 => 0, + ), + 'AZM' => array( + 0 => 'Azerbaijani Manat (1993-2006)', + 1 => 'AZM', + 2 => 2, + 3 => 0, + ), + 'BSD' => array( + 0 => 'Bahamian Dollar', + 1 => 'BSD', + 2 => 2, + 3 => 0, + ), + 'BHD' => array( + 0 => 'Bahraini Dinar', + 1 => 'BHD', + 2 => 3, + 3 => 0, + ), + 'BDT' => array( + 0 => 'Bangladeshi Taka', + 1 => 'BDT', + 2 => 2, + 3 => 0, + ), + 'BBD' => array( + 0 => 'Barbadian Dollar', + 1 => 'BBD', + 2 => 2, + 3 => 0, + ), + 'BYB' => array( + 0 => 'Belarusian New Ruble (1994-1999)', + 1 => 'BYB', + 2 => 2, + 3 => 0, + ), + 'BYR' => array( + 0 => 'Belarusian Ruble', + 1 => 'BYR', + 2 => 0, + 3 => 0, + ), + 'BEF' => array( + 0 => 'Belgian Franc', + 1 => 'BEF', + 2 => 2, + 3 => 0, + ), + 'BEC' => array( + 0 => 'Belgian Franc (convertible)', + 1 => 'BEC', + 2 => 2, + 3 => 0, + ), + 'BEL' => array( + 0 => 'Belgian Franc (financial)', + 1 => 'BEL', + 2 => 2, + 3 => 0, + ), + 'BZD' => array( + 0 => 'Belize Dollar', + 1 => 'BZD', + 2 => 2, + 3 => 0, + ), + 'BMD' => array( + 0 => 'Bermudan Dollar', + 1 => 'BMD', + 2 => 2, + 3 => 0, + ), + 'BTN' => array( + 0 => 'Bhutanese Ngultrum', + 1 => 'BTN', + 2 => 2, + 3 => 0, + ), + 'BOB' => array( + 0 => 'Bolivian Boliviano', + 1 => 'BOB', + 2 => 2, + 3 => 0, + ), + 'BOL' => array( + 0 => 'Bolivian Boliviano (1863-1963)', + 1 => 'BOL', + 2 => 2, + 3 => 0, + ), + 'BOV' => array( + 0 => 'Bolivian Mvdol', + 1 => 'BOV', + 2 => 2, + 3 => 0, + ), + 'BOP' => array( + 0 => 'Bolivian Peso', + 1 => 'BOP', + 2 => 2, + 3 => 0, + ), + 'BAM' => array( + 0 => 'Bosnia-Herzegovina Convertible Mark', + 1 => 'BAM', + 2 => 2, + 3 => 0, + ), + 'BAD' => array( + 0 => 'Bosnia-Herzegovina Dinar (1992-1994)', + 1 => 'BAD', + 2 => 2, + 3 => 0, + ), + 'BAN' => array( + 0 => 'Bosnia-Herzegovina New Dinar (1994-1997)', + 1 => 'BAN', + 2 => 2, + 3 => 0, + ), + 'BWP' => array( + 0 => 'Botswanan Pula', + 1 => 'BWP', + 2 => 2, + 3 => 0, + ), + 'BRC' => array( + 0 => 'Brazilian Cruzado (1986-1989)', + 1 => 'BRC', + 2 => 2, + 3 => 0, + ), + 'BRZ' => array( + 0 => 'Brazilian Cruzeiro (1942-1967)', + 1 => 'BRZ', + 2 => 2, + 3 => 0, + ), + 'BRE' => array( + 0 => 'Brazilian Cruzeiro (1990-1993)', + 1 => 'BRE', + 2 => 2, + 3 => 0, + ), + 'BRR' => array( + 0 => 'Brazilian Cruzeiro (1993-1994)', + 1 => 'BRR', + 2 => 2, + 3 => 0, + ), + 'BRN' => array( + 0 => 'Brazilian New Cruzado (1989-1990)', + 1 => 'BRN', + 2 => 2, + 3 => 0, + ), + 'BRB' => array( + 0 => 'Brazilian New Cruzeiro (1967-1986)', + 1 => 'BRB', + 2 => 2, + 3 => 0, + ), + 'BRL' => array( + 0 => 'Brazilian Real', + 1 => 'R$', + 2 => 2, + 3 => 0, + ), + 'GBP' => array( + 0 => 'British Pound Sterling', + 1 => '£', + 2 => 2, + 3 => 0, + ), + 'BND' => array( + 0 => 'Brunei Dollar', + 1 => 'BND', + 2 => 2, + 3 => 0, + ), + 'BGL' => array( + 0 => 'Bulgarian Hard Lev', + 1 => 'BGL', + 2 => 2, + 3 => 0, + ), + 'BGN' => array( + 0 => 'Bulgarian Lev', + 1 => 'BGN', + 2 => 2, + 3 => 0, + ), + 'BGO' => array( + 0 => 'Bulgarian Lev (1879-1952)', + 1 => 'BGO', + 2 => 2, + 3 => 0, + ), + 'BGM' => array( + 0 => 'Bulgarian Socialist Lev', + 1 => 'BGM', + 2 => 2, + 3 => 0, + ), + 'BUK' => array( + 0 => 'Burmese Kyat', + 1 => 'BUK', + 2 => 2, + 3 => 0, + ), + 'BIF' => array( + 0 => 'Burundian Franc', + 1 => 'BIF', + 2 => 0, + 3 => 0, + ), + 'KHR' => array( + 0 => 'Cambodian Riel', + 1 => 'KHR', + 2 => 2, + 3 => 0, + ), + 'CAD' => array( + 0 => 'Canadian Dollar', + 1 => 'CA$', + 2 => 2, + 3 => 0, + ), + 'CVE' => array( + 0 => 'Cape Verdean Escudo', + 1 => 'CVE', + 2 => 2, + 3 => 0, + ), + 'KYD' => array( + 0 => 'Cayman Islands Dollar', + 1 => 'KYD', + 2 => 2, + 3 => 0, + ), + 'XOF' => array( + 0 => 'CFA Franc BCEAO', + 1 => 'CFA', + 2 => 0, + 3 => 0, + ), + 'XAF' => array( + 0 => 'CFA Franc BEAC', + 1 => 'FCFA', + 2 => 0, + 3 => 0, + ), + 'XPF' => array( + 0 => 'CFP Franc', + 1 => 'CFPF', + 2 => 0, + 3 => 0, + ), + 'CLE' => array( + 0 => 'Chilean Escudo', + 1 => 'CLE', + 2 => 2, + 3 => 0, + ), + 'CLP' => array( + 0 => 'Chilean Peso', + 1 => 'CLP', + 2 => 0, + 3 => 0, + ), + 'CLF' => array( + 0 => 'Chilean Unit of Account (UF)', + 1 => 'CLF', + 2 => 0, + 3 => 0, + ), + 'CNX' => array( + 0 => 'Chinese People’s Bank Dollar', + 1 => 'CNX', + 2 => 2, + 3 => 0, + ), + 'CNY' => array( + 0 => 'Chinese Yuan', + 1 => 'CN¥', + 2 => 2, + 3 => 0, + ), + 'COP' => array( + 0 => 'Colombian Peso', + 1 => 'COP', + 2 => 0, + 3 => 0, + ), + 'COU' => array( + 0 => 'Colombian Real Value Unit', + 1 => 'COU', + 2 => 2, + 3 => 0, + ), + 'KMF' => array( + 0 => 'Comorian Franc', + 1 => 'KMF', + 2 => 0, + 3 => 0, + ), + 'CDF' => array( + 0 => 'Congolese Franc', + 1 => 'CDF', + 2 => 2, + 3 => 0, + ), + 'CRC' => array( + 0 => 'Costa Rican Colón', + 1 => 'CRC', + 2 => 0, + 3 => 0, + ), + 'HRD' => array( + 0 => 'Croatian Dinar', + 1 => 'HRD', + 2 => 2, + 3 => 0, + ), + 'HRK' => array( + 0 => 'Croatian Kuna', + 1 => 'HRK', + 2 => 2, + 3 => 0, + ), + 'CUC' => array( + 0 => 'Cuban Convertible Peso', + 1 => 'CUC', + 2 => 2, + 3 => 0, + ), + 'CUP' => array( + 0 => 'Cuban Peso', + 1 => 'CUP', + 2 => 2, + 3 => 0, + ), + 'CYP' => array( + 0 => 'Cypriot Pound', + 1 => 'CYP', + 2 => 2, + 3 => 0, + ), + 'CZK' => array( + 0 => 'Czech Republic Koruna', + 1 => 'CZK', + 2 => 2, + 3 => 0, + ), + 'CSK' => array( + 0 => 'Czechoslovak Hard Koruna', + 1 => 'CSK', + 2 => 2, + 3 => 0, + ), + 'DKK' => array( + 0 => 'Danish Krone', + 1 => 'DKK', + 2 => 2, + 3 => 0, + ), + 'DJF' => array( + 0 => 'Djiboutian Franc', + 1 => 'DJF', + 2 => 0, + 3 => 0, + ), + 'DOP' => array( + 0 => 'Dominican Peso', + 1 => 'DOP', + 2 => 2, + 3 => 0, + ), + 'NLG' => array( + 0 => 'Dutch Guilder', + 1 => 'NLG', + 2 => 2, + 3 => 0, + ), + 'XCD' => array( + 0 => 'East Caribbean Dollar', + 1 => 'EC$', + 2 => 2, + 3 => 0, + ), + 'DDM' => array( + 0 => 'East German Mark', + 1 => 'DDM', + 2 => 2, + 3 => 0, + ), + 'ECS' => array( + 0 => 'Ecuadorian Sucre', + 1 => 'ECS', + 2 => 2, + 3 => 0, + ), + 'ECV' => array( + 0 => 'Ecuadorian Unit of Constant Value', + 1 => 'ECV', + 2 => 2, + 3 => 0, + ), + 'EGP' => array( + 0 => 'Egyptian Pound', + 1 => 'EGP', + 2 => 2, + 3 => 0, + ), + 'GQE' => array( + 0 => 'Equatorial Guinean Ekwele', + 1 => 'GQE', + 2 => 2, + 3 => 0, + ), + 'ERN' => array( + 0 => 'Eritrean Nakfa', + 1 => 'ERN', + 2 => 2, + 3 => 0, + ), + 'EEK' => array( + 0 => 'Estonian Kroon', + 1 => 'EEK', + 2 => 2, + 3 => 0, + ), + 'ETB' => array( + 0 => 'Ethiopian Birr', + 1 => 'ETB', + 2 => 2, + 3 => 0, + ), + 'EUR' => array( + 0 => 'Euro', + 1 => '€', + 2 => 2, + 3 => 0, + ), + 'XBA' => array( + 0 => 'European Composite Unit', + 1 => 'XBA', + 2 => 2, + 3 => 0, + ), + 'XEU' => array( + 0 => 'European Currency Unit', + 1 => 'XEU', + 2 => 2, + 3 => 0, + ), + 'XBB' => array( + 0 => 'European Monetary Unit', + 1 => 'XBB', + 2 => 2, + 3 => 0, + ), + 'XBC' => array( + 0 => 'European Unit of Account (XBC)', + 1 => 'XBC', + 2 => 2, + 3 => 0, + ), + 'XBD' => array( + 0 => 'European Unit of Account (XBD)', + 1 => 'XBD', + 2 => 2, + 3 => 0, + ), + 'FKP' => array( + 0 => 'Falkland Islands Pound', + 1 => 'FKP', + 2 => 2, + 3 => 0, + ), + 'FJD' => array( + 0 => 'Fijian Dollar', + 1 => 'FJD', + 2 => 2, + 3 => 0, + ), + 'FIM' => array( + 0 => 'Finnish Markka', + 1 => 'FIM', + 2 => 2, + 3 => 0, + ), + 'FRF' => array( + 0 => 'French Franc', + 1 => 'FRF', + 2 => 2, + 3 => 0, + ), + 'XFO' => array( + 0 => 'French Gold Franc', + 1 => 'XFO', + 2 => 2, + 3 => 0, + ), + 'XFU' => array( + 0 => 'French UIC-Franc', + 1 => 'XFU', + 2 => 2, + 3 => 0, + ), + 'GMD' => array( + 0 => 'Gambian Dalasi', + 1 => 'GMD', + 2 => 2, + 3 => 0, + ), + 'GEK' => array( + 0 => 'Georgian Kupon Larit', + 1 => 'GEK', + 2 => 2, + 3 => 0, + ), + 'GEL' => array( + 0 => 'Georgian Lari', + 1 => 'GEL', + 2 => 2, + 3 => 0, + ), + 'DEM' => array( + 0 => 'German Mark', + 1 => 'DEM', + 2 => 2, + 3 => 0, + ), + 'GHS' => array( + 0 => 'Ghanaian Cedi', + 1 => 'GHS', + 2 => 2, + 3 => 0, + ), + 'GHC' => array( + 0 => 'Ghanaian Cedi (1979-2007)', + 1 => 'GHC', + 2 => 2, + 3 => 0, + ), + 'GIP' => array( + 0 => 'Gibraltar Pound', + 1 => 'GIP', + 2 => 2, + 3 => 0, + ), + 'XAU' => array( + 0 => 'Gold', + 1 => 'XAU', + 2 => 2, + 3 => 0, + ), + 'GRD' => array( + 0 => 'Greek Drachma', + 1 => 'GRD', + 2 => 2, + 3 => 0, + ), + 'GTQ' => array( + 0 => 'Guatemalan Quetzal', + 1 => 'GTQ', + 2 => 2, + 3 => 0, + ), + 'GWP' => array( + 0 => 'Guinea-Bissau Peso', + 1 => 'GWP', + 2 => 2, + 3 => 0, + ), + 'GNF' => array( + 0 => 'Guinean Franc', + 1 => 'GNF', + 2 => 0, + 3 => 0, + ), + 'GNS' => array( + 0 => 'Guinean Syli', + 1 => 'GNS', + 2 => 2, + 3 => 0, + ), + 'GYD' => array( + 0 => 'Guyanaese Dollar', + 1 => 'GYD', + 2 => 0, + 3 => 0, + ), + 'HTG' => array( + 0 => 'Haitian Gourde', + 1 => 'HTG', + 2 => 2, + 3 => 0, + ), + 'HNL' => array( + 0 => 'Honduran Lempira', + 1 => 'HNL', + 2 => 2, + 3 => 0, + ), + 'HKD' => array( + 0 => 'Hong Kong Dollar', + 1 => 'HK$', + 2 => 2, + 3 => 0, + ), + 'HUF' => array( + 0 => 'Hungarian Forint', + 1 => 'HUF', + 2 => 0, + 3 => 0, + ), + 'ISK' => array( + 0 => 'Icelandic Króna', + 1 => 'ISK', + 2 => 0, + 3 => 0, + ), + 'ISJ' => array( + 0 => 'Icelandic Króna (1918-1981)', + 1 => 'ISJ', + 2 => 2, + 3 => 0, + ), + 'INR' => array( + 0 => 'Indian Rupee', + 1 => '₹', + 2 => 2, + 3 => 0, + ), + 'IDR' => array( + 0 => 'Indonesian Rupiah', + 1 => 'IDR', + 2 => 0, + 3 => 0, + ), + 'IRR' => array( + 0 => 'Iranian Rial', + 1 => 'IRR', + 2 => 0, + 3 => 0, + ), + 'IQD' => array( + 0 => 'Iraqi Dinar', + 1 => 'IQD', + 2 => 0, + 3 => 0, + ), + 'IEP' => array( + 0 => 'Irish Pound', + 1 => 'IEP', + 2 => 2, + 3 => 0, + ), + 'ILS' => array( + 0 => 'Israeli New Sheqel', + 1 => '₪', + 2 => 2, + 3 => 0, + ), + 'ILP' => array( + 0 => 'Israeli Pound', + 1 => 'ILP', + 2 => 2, + 3 => 0, + ), + 'ILR' => array( + 0 => 'Israeli Sheqel (1980-1985)', + 1 => 'ILR', + 2 => 2, + 3 => 0, + ), + 'ITL' => array( + 0 => 'Italian Lira', + 1 => 'ITL', + 2 => 0, + 3 => 0, + ), + 'JMD' => array( + 0 => 'Jamaican Dollar', + 1 => 'JMD', + 2 => 2, + 3 => 0, + ), + 'JPY' => array( + 0 => 'Japanese Yen', + 1 => '¥', + 2 => 0, + 3 => 0, + ), + 'JOD' => array( + 0 => 'Jordanian Dinar', + 1 => 'JOD', + 2 => 3, + 3 => 0, + ), + 'KZT' => array( + 0 => 'Kazakhstani Tenge', + 1 => 'KZT', + 2 => 2, + 3 => 0, + ), + 'KES' => array( + 0 => 'Kenyan Shilling', + 1 => 'KES', + 2 => 2, + 3 => 0, + ), + 'KWD' => array( + 0 => 'Kuwaiti Dinar', + 1 => 'KWD', + 2 => 3, + 3 => 0, + ), + 'KGS' => array( + 0 => 'Kyrgystani Som', + 1 => 'KGS', + 2 => 2, + 3 => 0, + ), + 'LAK' => array( + 0 => 'Laotian Kip', + 1 => 'LAK', + 2 => 0, + 3 => 0, + ), + 'LVL' => array( + 0 => 'Latvian Lats', + 1 => 'LVL', + 2 => 2, + 3 => 0, + ), + 'LVR' => array( + 0 => 'Latvian Ruble', + 1 => 'LVR', + 2 => 2, + 3 => 0, + ), + 'LBP' => array( + 0 => 'Lebanese Pound', + 1 => 'LBP', + 2 => 0, + 3 => 0, + ), + 'LSL' => array( + 0 => 'Lesotho Loti', + 1 => 'LSL', + 2 => 2, + 3 => 0, + ), + 'LRD' => array( + 0 => 'Liberian Dollar', + 1 => 'LRD', + 2 => 2, + 3 => 0, + ), + 'LYD' => array( + 0 => 'Libyan Dinar', + 1 => 'LYD', + 2 => 3, + 3 => 0, + ), + 'LTL' => array( + 0 => 'Lithuanian Litas', + 1 => 'LTL', + 2 => 2, + 3 => 0, + ), + 'LTT' => array( + 0 => 'Lithuanian Talonas', + 1 => 'LTT', + 2 => 2, + 3 => 0, + ), + 'LUL' => array( + 0 => 'Luxembourg Financial Franc', + 1 => 'LUL', + 2 => 2, + 3 => 0, + ), + 'LUC' => array( + 0 => 'Luxembourgian Convertible Franc', + 1 => 'LUC', + 2 => 2, + 3 => 0, + ), + 'LUF' => array( + 0 => 'Luxembourgian Franc', + 1 => 'LUF', + 2 => 0, + 3 => 0, + ), + 'MOP' => array( + 0 => 'Macanese Pataca', + 1 => 'MOP', + 2 => 2, + 3 => 0, + ), + 'MKD' => array( + 0 => 'Macedonian Denar', + 1 => 'MKD', + 2 => 2, + 3 => 0, + ), + 'MKN' => array( + 0 => 'Macedonian Denar (1992-1993)', + 1 => 'MKN', + 2 => 2, + 3 => 0, + ), + 'MGA' => array( + 0 => 'Malagasy Ariary', + 1 => 'MGA', + 2 => 0, + 3 => 0, + ), + 'MGF' => array( + 0 => 'Malagasy Franc', + 1 => 'MGF', + 2 => 0, + 3 => 0, + ), + 'MWK' => array( + 0 => 'Malawian Kwacha', + 1 => 'MWK', + 2 => 2, + 3 => 0, + ), + 'MYR' => array( + 0 => 'Malaysian Ringgit', + 1 => 'MYR', + 2 => 2, + 3 => 0, + ), + 'MVR' => array( + 0 => 'Maldivian Rufiyaa', + 1 => 'MVR', + 2 => 2, + 3 => 0, + ), + 'MVP' => array( + 0 => 'Maldivian Rupee', + 1 => 'MVP', + 2 => 2, + 3 => 0, + ), + 'MLF' => array( + 0 => 'Malian Franc', + 1 => 'MLF', + 2 => 2, + 3 => 0, + ), + 'MTL' => array( + 0 => 'Maltese Lira', + 1 => 'MTL', + 2 => 2, + 3 => 0, + ), + 'MTP' => array( + 0 => 'Maltese Pound', + 1 => 'MTP', + 2 => 2, + 3 => 0, + ), + 'MRO' => array( + 0 => 'Mauritanian Ouguiya', + 1 => 'MRO', + 2 => 0, + 3 => 0, + ), + 'MUR' => array( + 0 => 'Mauritian Rupee', + 1 => 'MUR', + 2 => 0, + 3 => 0, + ), + 'MXV' => array( + 0 => 'Mexican Investment Unit', + 1 => 'MXV', + 2 => 2, + 3 => 0, + ), + 'MXN' => array( + 0 => 'Mexican Peso', + 1 => 'MX$', + 2 => 2, + 3 => 0, + ), + 'MXP' => array( + 0 => 'Mexican Silver Peso (1861-1992)', + 1 => 'MXP', + 2 => 2, + 3 => 0, + ), + 'MDC' => array( + 0 => 'Moldovan Cupon', + 1 => 'MDC', + 2 => 2, + 3 => 0, + ), + 'MDL' => array( + 0 => 'Moldovan Leu', + 1 => 'MDL', + 2 => 2, + 3 => 0, + ), + 'MCF' => array( + 0 => 'Monegasque Franc', + 1 => 'MCF', + 2 => 2, + 3 => 0, + ), + 'MNT' => array( + 0 => 'Mongolian Tugrik', + 1 => 'MNT', + 2 => 0, + 3 => 0, + ), + 'MAD' => array( + 0 => 'Moroccan Dirham', + 1 => 'MAD', + 2 => 2, + 3 => 0, + ), + 'MAF' => array( + 0 => 'Moroccan Franc', + 1 => 'MAF', + 2 => 2, + 3 => 0, + ), + 'MZE' => array( + 0 => 'Mozambican Escudo', + 1 => 'MZE', + 2 => 2, + 3 => 0, + ), + 'MZN' => array( + 0 => 'Mozambican Metical', + 1 => 'MZN', + 2 => 2, + 3 => 0, + ), + 'MZM' => array( + 0 => 'Mozambican Metical (1980-2006)', + 1 => 'MZM', + 2 => 2, + 3 => 0, + ), + 'MMK' => array( + 0 => 'Myanma Kyat', + 1 => 'MMK', + 2 => 0, + 3 => 0, + ), + 'NAD' => array( + 0 => 'Namibian Dollar', + 1 => 'NAD', + 2 => 2, + 3 => 0, + ), + 'NPR' => array( + 0 => 'Nepalese Rupee', + 1 => 'NPR', + 2 => 2, + 3 => 0, + ), + 'ANG' => array( + 0 => 'Netherlands Antillean Guilder', + 1 => 'ANG', + 2 => 2, + 3 => 0, + ), + 'TWD' => array( + 0 => 'New Taiwan Dollar', + 1 => 'NT$', + 2 => 2, + 3 => 0, + ), + 'NZD' => array( + 0 => 'New Zealand Dollar', + 1 => 'NZ$', + 2 => 2, + 3 => 0, + ), + 'NIO' => array( + 0 => 'Nicaraguan Córdoba', + 1 => 'NIO', + 2 => 2, + 3 => 0, + ), + 'NIC' => array( + 0 => 'Nicaraguan Córdoba (1988-1991)', + 1 => 'NIC', + 2 => 2, + 3 => 0, + ), + 'NGN' => array( + 0 => 'Nigerian Naira', + 1 => 'NGN', + 2 => 2, + 3 => 0, + ), + 'KPW' => array( + 0 => 'North Korean Won', + 1 => 'KPW', + 2 => 0, + 3 => 0, + ), + 'NOK' => array( + 0 => 'Norwegian Krone', + 1 => 'NOK', + 2 => 2, + 3 => 0, + ), + 'OMR' => array( + 0 => 'Omani Rial', + 1 => 'OMR', + 2 => 3, + 3 => 0, + ), + 'PKR' => array( + 0 => 'Pakistani Rupee', + 1 => 'PKR', + 2 => 0, + 3 => 0, + ), + 'XPD' => array( + 0 => 'Palladium', + 1 => 'XPD', + 2 => 2, + 3 => 0, + ), + 'PAB' => array( + 0 => 'Panamanian Balboa', + 1 => 'PAB', + 2 => 2, + 3 => 0, + ), + 'PGK' => array( + 0 => 'Papua New Guinean Kina', + 1 => 'PGK', + 2 => 2, + 3 => 0, + ), + 'PYG' => array( + 0 => 'Paraguayan Guarani', + 1 => 'PYG', + 2 => 0, + 3 => 0, + ), + 'PEI' => array( + 0 => 'Peruvian Inti', + 1 => 'PEI', + 2 => 2, + 3 => 0, + ), + 'PEN' => array( + 0 => 'Peruvian Nuevo Sol', + 1 => 'PEN', + 2 => 2, + 3 => 0, + ), + 'PES' => array( + 0 => 'Peruvian Sol (1863-1965)', + 1 => 'PES', + 2 => 2, + 3 => 0, + ), + 'PHP' => array( + 0 => 'Philippine Peso', + 1 => 'PHP', + 2 => 2, + 3 => 0, + ), + 'XPT' => array( + 0 => 'Platinum', + 1 => 'XPT', + 2 => 2, + 3 => 0, + ), + 'PLN' => array( + 0 => 'Polish Zloty', + 1 => 'PLN', + 2 => 2, + 3 => 0, + ), + 'PLZ' => array( + 0 => 'Polish Zloty (1950-1995)', + 1 => 'PLZ', + 2 => 2, + 3 => 0, + ), + 'PTE' => array( + 0 => 'Portuguese Escudo', + 1 => 'PTE', + 2 => 2, + 3 => 0, + ), + 'GWE' => array( + 0 => 'Portuguese Guinea Escudo', + 1 => 'GWE', + 2 => 2, + 3 => 0, + ), + 'QAR' => array( + 0 => 'Qatari Rial', + 1 => 'QAR', + 2 => 2, + 3 => 0, + ), + 'RHD' => array( + 0 => 'Rhodesian Dollar', + 1 => 'RHD', + 2 => 2, + 3 => 0, + ), + 'XRE' => array( + 0 => 'RINET Funds', + 1 => 'XRE', + 2 => 2, + 3 => 0, + ), + 'RON' => array( + 0 => 'Romanian Leu', + 1 => 'RON', + 2 => 2, + 3 => 0, + ), + 'ROL' => array( + 0 => 'Romanian Leu (1952-2006)', + 1 => 'ROL', + 2 => 2, + 3 => 0, + ), + 'RUB' => array( + 0 => 'Russian Ruble', + 1 => 'RUB', + 2 => 2, + 3 => 0, + ), + 'RUR' => array( + 0 => 'Russian Ruble (1991-1998)', + 1 => 'RUR', + 2 => 2, + 3 => 0, + ), + 'RWF' => array( + 0 => 'Rwandan Franc', + 1 => 'RWF', + 2 => 0, + 3 => 0, + ), + 'SHP' => array( + 0 => 'Saint Helena Pound', + 1 => 'SHP', + 2 => 2, + 3 => 0, + ), + 'SVC' => array( + 0 => 'Salvadoran Colón', + 1 => 'SVC', + 2 => 2, + 3 => 0, + ), + 'WST' => array( + 0 => 'Samoan Tala', + 1 => 'WST', + 2 => 2, + 3 => 0, + ), + 'STD' => array( + 0 => 'São Tomé and Príncipe Dobra', + 1 => 'STD', + 2 => 0, + 3 => 0, + ), + 'SAR' => array( + 0 => 'Saudi Riyal', + 1 => 'SAR', + 2 => 2, + 3 => 0, + ), + 'RSD' => array( + 0 => 'Serbian Dinar', + 1 => 'RSD', + 2 => 0, + 3 => 0, + ), + 'CSD' => array( + 0 => 'Serbian Dinar (2002-2006)', + 1 => 'CSD', + 2 => 2, + 3 => 0, + ), + 'SCR' => array( + 0 => 'Seychellois Rupee', + 1 => 'SCR', + 2 => 2, + 3 => 0, + ), + 'SLL' => array( + 0 => 'Sierra Leonean Leone', + 1 => 'SLL', + 2 => 0, + 3 => 0, + ), + 'XAG' => array( + 0 => 'Silver', + 1 => 'XAG', + 2 => 2, + 3 => 0, + ), + 'SGD' => array( + 0 => 'Singapore Dollar', + 1 => 'SGD', + 2 => 2, + 3 => 0, + ), + 'SKK' => array( + 0 => 'Slovak Koruna', + 1 => 'SKK', + 2 => 2, + 3 => 0, + ), + 'SIT' => array( + 0 => 'Slovenian Tolar', + 1 => 'SIT', + 2 => 2, + 3 => 0, + ), + 'SBD' => array( + 0 => 'Solomon Islands Dollar', + 1 => 'SBD', + 2 => 2, + 3 => 0, + ), + 'SOS' => array( + 0 => 'Somali Shilling', + 1 => 'SOS', + 2 => 0, + 3 => 0, + ), + 'ZAR' => array( + 0 => 'South African Rand', + 1 => 'ZAR', + 2 => 2, + 3 => 0, + ), + 'ZAL' => array( + 0 => 'South African Rand (financial)', + 1 => 'ZAL', + 2 => 2, + 3 => 0, + ), + 'KRH' => array( + 0 => 'South Korean Hwan (1953-1962)', + 1 => 'KRH', + 2 => 2, + 3 => 0, + ), + 'KRW' => array( + 0 => 'South Korean Won', + 1 => '₩', + 2 => 0, + 3 => 0, + ), + 'KRO' => array( + 0 => 'South Korean Won (1945-1953)', + 1 => 'KRO', + 2 => 2, + 3 => 0, + ), + 'SSP' => array( + 0 => 'South Sudanese Pound', + 1 => 'SSP', + 2 => 2, + 3 => 0, + ), + 'SUR' => array( + 0 => 'Soviet Rouble', + 1 => 'SUR', + 2 => 2, + 3 => 0, + ), + 'ESP' => array( + 0 => 'Spanish Peseta', + 1 => 'ESP', + 2 => 0, + 3 => 0, + ), + 'ESA' => array( + 0 => 'Spanish Peseta (A account)', + 1 => 'ESA', + 2 => 2, + 3 => 0, + ), + 'ESB' => array( + 0 => 'Spanish Peseta (convertible account)', + 1 => 'ESB', + 2 => 2, + 3 => 0, + ), + 'XDR' => array( + 0 => 'Special Drawing Rights', + 1 => 'XDR', + 2 => 2, + 3 => 0, + ), + 'LKR' => array( + 0 => 'Sri Lankan Rupee', + 1 => 'LKR', + 2 => 2, + 3 => 0, + ), + 'XSU' => array( + 0 => 'Sucre', + 1 => 'XSU', + 2 => 2, + 3 => 0, + ), + 'SDD' => array( + 0 => 'Sudanese Dinar (1992-2007)', + 1 => 'SDD', + 2 => 2, + 3 => 0, + ), + 'SDG' => array( + 0 => 'Sudanese Pound', + 1 => 'SDG', + 2 => 2, + 3 => 0, + ), + 'SDP' => array( + 0 => 'Sudanese Pound (1957-1998)', + 1 => 'SDP', + 2 => 2, + 3 => 0, + ), + 'SRD' => array( + 0 => 'Surinamese Dollar', + 1 => 'SRD', + 2 => 2, + 3 => 0, + ), + 'SRG' => array( + 0 => 'Surinamese Guilder', + 1 => 'SRG', + 2 => 2, + 3 => 0, + ), + 'SZL' => array( + 0 => 'Swazi Lilangeni', + 1 => 'SZL', + 2 => 2, + 3 => 0, + ), + 'SEK' => array( + 0 => 'Swedish Krona', + 1 => 'SEK', + 2 => 2, + 3 => 0, + ), + 'CHF' => array( + 0 => 'Swiss Franc', + 1 => 'CHF', + 2 => 2, + 3 => 5, + ), + 'SYP' => array( + 0 => 'Syrian Pound', + 1 => 'SYP', + 2 => 0, + 3 => 0, + ), + 'TJR' => array( + 0 => 'Tajikistani Ruble', + 1 => 'TJR', + 2 => 2, + 3 => 0, + ), + 'TJS' => array( + 0 => 'Tajikistani Somoni', + 1 => 'TJS', + 2 => 2, + 3 => 0, + ), + 'TZS' => array( + 0 => 'Tanzanian Shilling', + 1 => 'TZS', + 2 => 0, + 3 => 0, + ), + 'XTS' => array( + 0 => 'Testing Currency Code', + 1 => 'XTS', + 2 => 2, + 3 => 0, + ), + 'THB' => array( + 0 => 'Thai Baht', + 1 => '฿', + 2 => 2, + 3 => 0, + ), + 'TPE' => array( + 0 => 'Timorese Escudo', + 1 => 'TPE', + 2 => 2, + 3 => 0, + ), + 'TOP' => array( + 0 => 'Tongan Paʻanga', + 1 => 'TOP', + 2 => 2, + 3 => 0, + ), + 'TTD' => array( + 0 => 'Trinidad and Tobago Dollar', + 1 => 'TTD', + 2 => 2, + 3 => 0, + ), + 'TND' => array( + 0 => 'Tunisian Dinar', + 1 => 'TND', + 2 => 3, + 3 => 0, + ), + 'TRY' => array( + 0 => 'Turkish Lira', + 1 => 'TRY', + 2 => 2, + 3 => 0, + ), + 'TRL' => array( + 0 => 'Turkish Lira (1922-2005)', + 1 => 'TRL', + 2 => 0, + 3 => 0, + ), + 'TMT' => array( + 0 => 'Turkmenistani Manat', + 1 => 'TMT', + 2 => 2, + 3 => 0, + ), + 'TMM' => array( + 0 => 'Turkmenistani Manat (1993-2009)', + 1 => 'TMM', + 2 => 0, + 3 => 0, + ), + 'UGX' => array( + 0 => 'Ugandan Shilling', + 1 => 'UGX', + 2 => 0, + 3 => 0, + ), + 'UGS' => array( + 0 => 'Ugandan Shilling (1966-1987)', + 1 => 'UGS', + 2 => 2, + 3 => 0, + ), + 'UAH' => array( + 0 => 'Ukrainian Hryvnia', + 1 => 'UAH', + 2 => 2, + 3 => 0, + ), + 'UAK' => array( + 0 => 'Ukrainian Karbovanets', + 1 => 'UAK', + 2 => 2, + 3 => 0, + ), + 'AED' => array( + 0 => 'United Arab Emirates Dirham', + 1 => 'AED', + 2 => 2, + 3 => 0, + ), + 'XXX' => array( + 0 => 'Unknown Currency', + 1 => 'XXX', + 2 => 2, + 3 => 0, + ), + 'UYU' => array( + 0 => 'Uruguayan Peso', + 1 => 'UYU', + 2 => 2, + 3 => 0, + ), + 'UYP' => array( + 0 => 'Uruguayan Peso (1975-1993)', + 1 => 'UYP', + 2 => 2, + 3 => 0, + ), + 'UYI' => array( + 0 => 'Uruguayan Peso (Indexed Units)', + 1 => 'UYI', + 2 => 2, + 3 => 0, + ), + 'USD' => array( + 0 => 'US Dollar', + 1 => '$', + 2 => 2, + 3 => 0, + ), + 'USN' => array( + 0 => 'US Dollar (Next day)', + 1 => 'USN', + 2 => 2, + 3 => 0, + ), + 'USS' => array( + 0 => 'US Dollar (Same day)', + 1 => 'USS', + 2 => 2, + 3 => 0, + ), + 'UZS' => array( + 0 => 'Uzbekistan Som', + 1 => 'UZS', + 2 => 0, + 3 => 0, + ), + 'VUV' => array( + 0 => 'Vanuatu Vatu', + 1 => 'VUV', + 2 => 0, + 3 => 0, + ), + 'VEF' => array( + 0 => 'Venezuelan Bolívar', + 1 => 'VEF', + 2 => 2, + 3 => 0, + ), + 'VEB' => array( + 0 => 'Venezuelan Bolívar (1871-2008)', + 1 => 'VEB', + 2 => 2, + 3 => 0, + ), + 'VND' => array( + 0 => 'Vietnamese Dong', + 1 => '₫', + 2 => 0, + 3 => 0, + ), + 'VNN' => array( + 0 => 'Vietnamese Dong (1978-1985)', + 1 => 'VNN', + 2 => 2, + 3 => 0, + ), + 'CHE' => array( + 0 => 'WIR Euro', + 1 => 'CHE', + 2 => 2, + 3 => 0, + ), + 'CHW' => array( + 0 => 'WIR Franc', + 1 => 'CHW', + 2 => 2, + 3 => 0, + ), + 'YDD' => array( + 0 => 'Yemeni Dinar', + 1 => 'YDD', + 2 => 2, + 3 => 0, + ), + 'YER' => array( + 0 => 'Yemeni Rial', + 1 => 'YER', + 2 => 0, + 3 => 0, + ), + 'YUN' => array( + 0 => 'Yugoslavian Convertible Dinar (1990-1992)', + 1 => 'YUN', + 2 => 2, + 3 => 0, + ), + 'YUD' => array( + 0 => 'Yugoslavian Hard Dinar (1966-1990)', + 1 => 'YUD', + 2 => 2, + 3 => 0, + ), + 'YUM' => array( + 0 => 'Yugoslavian New Dinar (1994-2002)', + 1 => 'YUM', + 2 => 2, + 3 => 0, + ), + 'YUR' => array( + 0 => 'Yugoslavian Reformed Dinar (1992-1993)', + 1 => 'YUR', + 2 => 2, + 3 => 0, + ), + 'ZRN' => array( + 0 => 'Zairean New Zaire (1993-1998)', + 1 => 'ZRN', + 2 => 2, + 3 => 0, + ), + 'ZRZ' => array( + 0 => 'Zairean Zaire (1971-1993)', + 1 => 'ZRZ', + 2 => 2, + 3 => 0, + ), + 'ZMK' => array( + 0 => 'Zambian Kwacha', + 1 => 'ZMK', + 2 => 0, + 3 => 0, + ), + 'ZWD' => array( + 0 => 'Zimbabwean Dollar (1980-2008)', + 1 => 'ZWD', + 2 => 0, + 3 => 0, + ), + 'ZWR' => array( + 0 => 'Zimbabwean Dollar (2008)', + 1 => 'ZWR', + 2 => 2, + 3 => 0, + ), + 'ZWL' => array( + 0 => 'Zimbabwean Dollar (2009)', + 1 => 'ZWL', + 2 => 2, + 3 => 0, + ), + ), +); diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/en.php b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/en.php new file mode 100644 index 00000000..9d2ff140 --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/en.php @@ -0,0 +1,750 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'Languages' => array( + 'ab' => 'Abkhazian', + 'ace' => 'Achinese', + 'ach' => 'Acoli', + 'ada' => 'Adangme', + 'ady' => 'Adyghe', + 'aa' => 'Afar', + 'afh' => 'Afrihili', + 'af' => 'Afrikaans', + 'afa' => 'Afro-Asiatic Language', + 'agq' => 'Aghem', + 'ain' => 'Ainu', + 'ak' => 'Akan', + 'akk' => 'Akkadian', + 'bss' => 'Akoose', + 'sq' => 'Albanian', + 'ale' => 'Aleut', + 'alg' => 'Algonquian Language', + 'tut' => 'Altaic Language', + 'am' => 'Amharic', + 'egy' => 'Ancient Egyptian', + 'grc' => 'Ancient Greek', + 'anp' => 'Angika', + 'apa' => 'Apache Language', + 'ar' => 'Arabic', + 'an' => 'Aragonese', + 'arc' => 'Aramaic', + 'arp' => 'Arapaho', + 'arw' => 'Arawak', + 'hy' => 'Armenian', + 'rup' => 'Aromanian', + 'art' => 'Artificial Language', + 'as' => 'Assamese', + 'ast' => 'Asturian', + 'asa' => 'Asu', + 'ath' => 'Athapascan Language', + 'cch' => 'Atsam', + 'en_AU' => 'Australian English', + 'aus' => 'Australian Language', + 'de_AT' => 'Austrian German', + 'map' => 'Austronesian Language', + 'av' => 'Avaric', + 'ae' => 'Avestan', + 'awa' => 'Awadhi', + 'ay' => 'Aymara', + 'az' => 'Azerbaijani', + 'ksf' => 'Bafia', + 'bfd' => 'Bafut', + 'ban' => 'Balinese', + 'bat' => 'Baltic Language', + 'bal' => 'Baluchi', + 'bm' => 'Bambara', + 'bai' => 'Bamileke Language', + 'bax' => 'Bamun', + 'bad' => 'Banda', + 'bnt' => 'Bantu', + 'bas' => 'Basaa', + 'ba' => 'Bashkir', + 'eu' => 'Basque', + 'btk' => 'Batak', + 'bej' => 'Beja', + 'be' => 'Belarusian', + 'bem' => 'Bemba', + 'bez' => 'Bena', + 'bn' => 'Bengali', + 'ber' => 'Berber', + 'bho' => 'Bhojpuri', + 'bh' => 'Bihari', + 'bik' => 'Bikol', + 'bin' => 'Bini', + 'bi' => 'Bislama', + 'byn' => 'Blin', + 'zbl' => 'Blissymbols', + 'brx' => 'Bodo', + 'bs' => 'Bosnian', + 'bra' => 'Braj', + 'pt_BR' => 'Brazilian Portuguese', + 'br' => 'Breton', + 'en_GB' => 'British English', + 'bug' => 'Buginese', + 'bg' => 'Bulgarian', + 'bum' => 'Bulu', + 'bua' => 'Buriat', + 'my' => 'Burmese', + 'cad' => 'Caddo', + 'en_CA' => 'Canadian English', + 'fr_CA' => 'Canadian French', + 'yue' => 'Cantonese', + 'car' => 'Carib', + 'ca' => 'Catalan', + 'cau' => 'Caucasian Language', + 'cay' => 'Cayuga', + 'ceb' => 'Cebuano', + 'cel' => 'Celtic Language', + 'cai' => 'Central American Indian Language', + 'tzm' => 'Central Atlas Tamazight', + 'shu' => 'Chadian Arabic', + 'chg' => 'Chagatai', + 'cmc' => 'Chamic Language', + 'ch' => 'Chamorro', + 'ce' => 'Chechen', + 'chr' => 'Cherokee', + 'chy' => 'Cheyenne', + 'chb' => 'Chibcha', + 'cgg' => 'Chiga', + 'zh' => 'Chinese', + 'chn' => 'Chinook Jargon', + 'chp' => 'Chipewyan', + 'cho' => 'Choctaw', + 'cu' => 'Church Slavic', + 'chk' => 'Chuukese', + 'cv' => 'Chuvash', + 'nwc' => 'Classical Newari', + 'syc' => 'Classical Syriac', + 'ksh' => 'Colognian', + 'swb' => 'Comorian', + 'swc' => 'Congo Swahili', + 'cop' => 'Coptic', + 'kw' => 'Cornish', + 'co' => 'Corsican', + 'cr' => 'Cree', + 'mus' => 'Creek', + 'crp' => 'Creole or Pidgin', + 'crh' => 'Crimean Turkish', + 'hr' => 'Croatian', + 'cus' => 'Cushitic Language', + 'cs' => 'Czech', + 'dak' => 'Dakota', + 'da' => 'Danish', + 'dar' => 'Dargwa', + 'day' => 'Dayak', + 'dzg' => 'Dazaga', + 'del' => 'Delaware', + 'din' => 'Dinka', + 'dv' => 'Divehi', + 'doi' => 'Dogri', + 'dgr' => 'Dogrib', + 'dra' => 'Dravidian Language', + 'dua' => 'Duala', + 'nl' => 'Dutch', + 'dyu' => 'Dyula', + 'dz' => 'Dzongkha', + 'frs' => 'Eastern Frisian', + 'efi' => 'Efik', + 'eka' => 'Ekajuk', + 'elx' => 'Elamite', + 'ebu' => 'Embu', + 'en' => 'English', + 'cpe' => 'English-based Creole or Pidgin', + 'myv' => 'Erzya', + 'eo' => 'Esperanto', + 'et' => 'Estonian', + 'pt_PT' => 'European Portuguese', + 'es_ES' => 'European Spanish', + 'ee' => 'Ewe', + 'ewo' => 'Ewondo', + 'fan' => 'Fang', + 'fat' => 'Fanti', + 'fo' => 'Faroese', + 'fj' => 'Fijian', + 'fil' => 'Filipino', + 'fi' => 'Finnish', + 'fiu' => 'Finno-Ugrian Language', + 'nl_BE' => 'Flemish', + 'fon' => 'Fon', + 'fr' => 'French', + 'cpf' => 'French-based Creole or Pidgin', + 'fur' => 'Friulian', + 'ff' => 'Fulah', + 'gaa' => 'Ga', + 'gl' => 'Galician', + 'lg' => 'Ganda', + 'gay' => 'Gayo', + 'gba' => 'Gbaya', + 'gez' => 'Geez', + 'ka' => 'Georgian', + 'de' => 'German', + 'gem' => 'Germanic Language', + 'bbj' => 'Ghomala', + 'gil' => 'Gilbertese', + 'gon' => 'Gondi', + 'gor' => 'Gorontalo', + 'got' => 'Gothic', + 'grb' => 'Grebo', + 'el' => 'Greek', + 'gn' => 'Guarani', + 'gu' => 'Gujarati', + 'guz' => 'Gusii', + 'gwi' => 'Gwichʼin', + 'hai' => 'Haida', + 'ht' => 'Haitian', + 'ha' => 'Hausa', + 'haw' => 'Hawaiian', + 'he' => 'Hebrew', + 'hz' => 'Herero', + 'hil' => 'Hiligaynon', + 'him' => 'Himachali', + 'hi' => 'Hindi', + 'ho' => 'Hiri Motu', + 'hit' => 'Hittite', + 'hmn' => 'Hmong', + 'hu' => 'Hungarian', + 'hup' => 'Hupa', + 'iba' => 'Iban', + 'ibb' => 'Ibibio', + 'is' => 'Icelandic', + 'io' => 'Ido', + 'ig' => 'Igbo', + 'ijo' => 'Ijo', + 'ilo' => 'Iloko', + 'smn' => 'Inari Sami', + 'inc' => 'Indic Language', + 'ine' => 'Indo-European Language', + 'id' => 'Indonesian', + 'inh' => 'Ingush', + 'ia' => 'Interlingua', + 'ie' => 'Interlingue', + 'iu' => 'Inuktitut', + 'ik' => 'Inupiaq', + 'ira' => 'Iranian Language', + 'ga' => 'Irish', + 'iro' => 'Iroquoian Language', + 'it' => 'Italian', + 'ja' => 'Japanese', + 'jv' => 'Javanese', + 'kaj' => 'Jju', + 'dyo' => 'Jola-Fonyi', + 'jrb' => 'Judeo-Arabic', + 'jpr' => 'Judeo-Persian', + 'kbd' => 'Kabardian', + 'kea' => 'Kabuverdianu', + 'kab' => 'Kabyle', + 'kac' => 'Kachin', + 'kkj' => 'Kako', + 'kl' => 'Kalaallisut', + 'kln' => 'Kalenjin', + 'xal' => 'Kalmyk', + 'kam' => 'Kamba', + 'kbl' => 'Kanembu', + 'kn' => 'Kannada', + 'kr' => 'Kanuri', + 'kaa' => 'Kara-Kalpak', + 'krc' => 'Karachay-Balkar', + 'krl' => 'Karelian', + 'kar' => 'Karen', + 'ks' => 'Kashmiri', + 'csb' => 'Kashubian', + 'kaw' => 'Kawi', + 'kk' => 'Kazakh', + 'kha' => 'Khasi', + 'km' => 'Khmer', + 'khi' => 'Khoisan Language', + 'kho' => 'Khotanese', + 'ki' => 'Kikuyu', + 'kmb' => 'Kimbundu', + 'rw' => 'Kinyarwanda', + 'ky' => 'Kirghiz', + 'tlh' => 'Klingon', + 'bkm' => 'Kom', + 'kv' => 'Komi', + 'kg' => 'Kongo', + 'kok' => 'Konkani', + 'ko' => 'Korean', + 'kfo' => 'Koro', + 'kos' => 'Kosraean', + 'khq' => 'Koyra Chiini', + 'ses' => 'Koyraboro Senni', + 'kpe' => 'Kpelle', + 'kro' => 'Kru', + 'kj' => 'Kuanyama', + 'kum' => 'Kumyk', + 'ku' => 'Kurdish', + 'kru' => 'Kurukh', + 'kut' => 'Kutenai', + 'nmg' => 'Kwasio', + 'lad' => 'Ladino', + 'lah' => 'Lahnda', + 'lam' => 'Lamba', + 'lag' => 'Langi', + 'lo' => 'Lao', + 'la' => 'Latin', + 'es_419' => 'Latin American Spanish', + 'lv' => 'Latvian', + 'lez' => 'Lezghian', + 'li' => 'Limburgish', + 'ln' => 'Lingala', + 'lt' => 'Lithuanian', + 'jbo' => 'Lojban', + 'nds' => 'Low German', + 'dsb' => 'Lower Sorbian', + 'loz' => 'Lozi', + 'lu' => 'Luba-Katanga', + 'lua' => 'Luba-Lulua', + 'lui' => 'Luiseno', + 'smj' => 'Lule Sami', + 'lun' => 'Lunda', + 'luo' => 'Luo', + 'lb' => 'Luxembourgish', + 'luy' => 'Luyia', + 'mde' => 'Maba', + 'mk' => 'Macedonian', + 'jmc' => 'Machame', + 'mad' => 'Madurese', + 'maf' => 'Mafa', + 'mag' => 'Magahi', + 'mai' => 'Maithili', + 'mak' => 'Makasar', + 'mgh' => 'Makhuwa-Meetto', + 'kde' => 'Makonde', + 'mg' => 'Malagasy', + 'ms' => 'Malay', + 'ml' => 'Malayalam', + 'mt' => 'Maltese', + 'mnc' => 'Manchu', + 'mdr' => 'Mandar', + 'man' => 'Mandingo', + 'mni' => 'Manipuri', + 'mno' => 'Manobo Language', + 'gv' => 'Manx', + 'mi' => 'Maori', + 'arn' => 'Mapuche', + 'mr' => 'Marathi', + 'chm' => 'Mari', + 'mh' => 'Marshallese', + 'mwr' => 'Marwari', + 'mas' => 'Masai', + 'myn' => 'Mayan Language', + 'byv' => 'Medumba', + 'men' => 'Mende', + 'mer' => 'Meru', + 'mgo' => 'Meta\'', + 'mic' => 'Micmac', + 'dum' => 'Middle Dutch', + 'enm' => 'Middle English', + 'frm' => 'Middle French', + 'gmh' => 'Middle High German', + 'mga' => 'Middle Irish', + 'min' => 'Minangkabau', + 'mwl' => 'Mirandese', + 'mis' => 'Miscellaneous Language', + 'lus' => 'Mizo', + 'ar_001' => 'Modern Standard Arabic', + 'moh' => 'Mohawk', + 'mdf' => 'Moksha', + 'mo' => 'Moldavian', + 'mkh' => 'Mon-Khmer Language', + 'lol' => 'Mongo', + 'mn' => 'Mongolian', + 'mfe' => 'Morisyen', + 'mos' => 'Mossi', + 'mun' => 'Munda Language', + 'mua' => 'Mundang', + 'mye' => 'Myene', + 'nqo' => 'N’Ko', + 'nah' => 'Nahuatl', + 'naq' => 'Nama', + 'na' => 'Nauru', + 'nv' => 'Navajo', + 'ng' => 'Ndonga', + 'nap' => 'Neapolitan', + 'ne' => 'Nepali', + 'new' => 'Newari', + 'sba' => 'Ngambay', + 'nnh' => 'Ngiemboon', + 'jgo' => 'Ngomba', + 'nia' => 'Nias', + 'nic' => 'Niger-Kordofanian Language', + 'ssa' => 'Nilo-Saharan Language', + 'niu' => 'Niuean', + 'zxx' => 'No linguistic content', + 'nog' => 'Nogai', + 'nai' => 'North American Indian Language', + 'nd' => 'North Ndebele', + 'frr' => 'Northern Frisian', + 'se' => 'Northern Sami', + 'nso' => 'Northern Sotho', + 'no' => 'Norwegian', + 'nb' => 'Norwegian Bokmål', + 'nn' => 'Norwegian Nynorsk', + 'nub' => 'Nubian Language', + 'nus' => 'Nuer', + 'nym' => 'Nyamwezi', + 'ny' => 'Nyanja', + 'nyn' => 'Nyankole', + 'tog' => 'Nyasa Tonga', + 'nyo' => 'Nyoro', + 'nzi' => 'Nzima', + 'oc' => 'Occitan', + 'oj' => 'Ojibwa', + 'ang' => 'Old English', + 'fro' => 'Old French', + 'goh' => 'Old High German', + 'sga' => 'Old Irish', + 'non' => 'Old Norse', + 'peo' => 'Old Persian', + 'pro' => 'Old Provençal', + 'or' => 'Oriya', + 'om' => 'Oromo', + 'osa' => 'Osage', + 'os' => 'Ossetic', + 'oto' => 'Otomian Language', + 'ota' => 'Ottoman Turkish', + 'pal' => 'Pahlavi', + 'pau' => 'Palauan', + 'pi' => 'Pali', + 'pam' => 'Pampanga', + 'pag' => 'Pangasinan', + 'pap' => 'Papiamento', + 'paa' => 'Papuan Language', + 'ps' => 'Pashto', + 'fa' => 'Persian', + 'phi' => 'Philippine Language', + 'phn' => 'Phoenician', + 'pon' => 'Pohnpeian', + 'pl' => 'Polish', + 'pt' => 'Portuguese', + 'cpp' => 'Portuguese-based Creole or Pidgin', + 'pra' => 'Prakrit Language', + 'pa' => 'Punjabi', + 'qu' => 'Quechua', + 'raj' => 'Rajasthani', + 'rap' => 'Rapanui', + 'rar' => 'Rarotongan', + 'roa' => 'Romance Language', + 'ro' => 'Romanian', + 'rm' => 'Romansh', + 'rom' => 'Romany', + 'rof' => 'Rombo', + 'root' => 'Root', + 'rn' => 'Rundi', + 'ru' => 'Russian', + 'rwk' => 'Rwa', + 'ssy' => 'Saho', + 'sah' => 'Sakha', + 'sal' => 'Salishan Language', + 'sam' => 'Samaritan Aramaic', + 'saq' => 'Samburu', + 'smi' => 'Sami Language', + 'sm' => 'Samoan', + 'sad' => 'Sandawe', + 'sg' => 'Sango', + 'sbp' => 'Sangu', + 'sa' => 'Sanskrit', + 'sat' => 'Santali', + 'sc' => 'Sardinian', + 'sas' => 'Sasak', + 'sco' => 'Scots', + 'gd' => 'Scottish Gaelic', + 'sel' => 'Selkup', + 'sem' => 'Semitic Language', + 'seh' => 'Sena', + 'see' => 'Seneca', + 'sr' => 'Serbian', + 'sh' => 'Serbo-Croatian', + 'srr' => 'Serer', + 'ksb' => 'Shambala', + 'shn' => 'Shan', + 'sn' => 'Shona', + 'ii' => 'Sichuan Yi', + 'scn' => 'Sicilian', + 'sid' => 'Sidamo', + 'sgn' => 'Sign Language', + 'bla' => 'Siksika', + 'zh_Hans' => 'Simplified Chinese', + 'sd' => 'Sindhi', + 'si' => 'Sinhala', + 'sit' => 'Sino-Tibetan Language', + 'sio' => 'Siouan Language', + 'sms' => 'Skolt Sami', + 'den' => 'Slave', + 'sla' => 'Slavic Language', + 'sk' => 'Slovak', + 'sl' => 'Slovenian', + 'xog' => 'Soga', + 'sog' => 'Sogdien', + 'so' => 'Somali', + 'son' => 'Songhai', + 'snk' => 'Soninke', + 'ckb' => 'Sorani Kurdish', + 'wen' => 'Sorbian Language', + 'sai' => 'South American Indian Language', + 'nr' => 'South Ndebele', + 'alt' => 'Southern Altai', + 'sma' => 'Southern Sami', + 'st' => 'Southern Sotho', + 'es' => 'Spanish', + 'srn' => 'Sranan Tongo', + 'suk' => 'Sukuma', + 'sux' => 'Sumerian', + 'su' => 'Sundanese', + 'sus' => 'Susu', + 'sw' => 'Swahili', + 'ss' => 'Swati', + 'sv' => 'Swedish', + 'fr_CH' => 'Swiss French', + 'gsw' => 'Swiss German', + 'de_CH' => 'Swiss High German', + 'syr' => 'Syriac', + 'shi' => 'Tachelhit', + 'tl' => 'Tagalog', + 'ty' => 'Tahitian', + 'tai' => 'Tai Language', + 'dav' => 'Taita', + 'tg' => 'Tajik', + 'tmh' => 'Tamashek', + 'ta' => 'Tamil', + 'trv' => 'Taroko', + 'twq' => 'Tasawaq', + 'tt' => 'Tatar', + 'te' => 'Telugu', + 'ter' => 'Tereno', + 'teo' => 'Teso', + 'tet' => 'Tetum', + 'th' => 'Thai', + 'bo' => 'Tibetan', + 'tig' => 'Tigre', + 'ti' => 'Tigrinya', + 'tem' => 'Timne', + 'tiv' => 'Tiv', + 'tli' => 'Tlingit', + 'tpi' => 'Tok Pisin', + 'tkl' => 'Tokelau', + 'to' => 'Tongan', + 'zh_Hant' => 'Traditional Chinese', + 'tsi' => 'Tsimshian', + 'ts' => 'Tsonga', + 'tn' => 'Tswana', + 'tum' => 'Tumbuka', + 'tup' => 'Tupi Language', + 'tr' => 'Turkish', + 'tk' => 'Turkmen', + 'tvl' => 'Tuvalu', + 'tyv' => 'Tuvinian', + 'tw' => 'Twi', + 'kcg' => 'Tyap', + 'en_US' => 'U.S. English', + 'udm' => 'Udmurt', + 'uga' => 'Ugaritic', + 'ug' => 'Uighur', + 'uk' => 'Ukrainian', + 'umb' => 'Umbundu', + 'und' => 'Unknown Language', + 'hsb' => 'Upper Sorbian', + 'ur' => 'Urdu', + 'uz' => 'Uzbek', + 'vai' => 'Vai', + 've' => 'Venda', + 'vi' => 'Vietnamese', + 'vo' => 'Volapük', + 'vot' => 'Votic', + 'vun' => 'Vunjo', + 'wak' => 'Wakashan Language', + 'wa' => 'Walloon', + 'wae' => 'Walser', + 'war' => 'Waray', + 'was' => 'Washo', + 'cy' => 'Welsh', + 'fy' => 'Western Frisian', + 'wal' => 'Wolaytta', + 'wo' => 'Wolof', + 'xh' => 'Xhosa', + 'yav' => 'Yangben', + 'yao' => 'Yao', + 'yap' => 'Yapese', + 'ybb' => 'Yemba', + 'yi' => 'Yiddish', + 'yo' => 'Yoruba', + 'ypk' => 'Yupik Language', + 'znd' => 'Zande', + 'zap' => 'Zapotec', + 'dje' => 'Zarma', + 'zza' => 'Zaza', + 'zen' => 'Zenaga', + 'za' => 'Zhuang', + 'zu' => 'Zulu', + 'zun' => 'Zuni', + ), + 'Scripts' => array( + 'Afak' => 'Afaka', + 'Hluw' => 'Anatolian Hieroglyphs', + 'Arab' => 'Arabic', + 'Armn' => 'Armenian', + 'Avst' => 'Avestan', + 'Bali' => 'Balinese', + 'Bamu' => 'Bamum', + 'Bass' => 'Bassa Vah', + 'Batk' => 'Batak', + 'Beng' => 'Bengali', + 'Blis' => 'Blissymbols', + 'Phlv' => 'Book Pahlavi', + 'Bopo' => 'Bopomofo', + 'Brah' => 'Brahmi', + 'Brai' => 'Braille', + 'Bugi' => 'Buginese', + 'Buhd' => 'Buhid', + 'Cari' => 'Carian', + 'Cakm' => 'Chakma', + 'Cham' => 'Cham', + 'Cher' => 'Cherokee', + 'Cirt' => 'Cirth', + 'Zyyy' => 'Common', + 'Copt' => 'Coptic', + 'Cprt' => 'Cypriot', + 'Cyrl' => 'Cyrillic', + 'Dsrt' => 'Deseret', + 'Deva' => 'Devanagari', + 'Dupl' => 'Duployan shorthand', + 'Syrn' => 'Eastern Syriac', + 'Egyd' => 'Egyptian demotic', + 'Egyh' => 'Egyptian hieratic', + 'Egyp' => 'Egyptian hieroglyphs', + 'Syre' => 'Estrangelo Syriac', + 'Ethi' => 'Ethiopic', + 'Latf' => 'Fraktur Latin', + 'Lisu' => 'Fraser', + 'Latg' => 'Gaelic Latin', + 'Geor' => 'Georgian', + 'Geok' => 'Georgian Khutsuri', + 'Glag' => 'Glagolitic', + 'Goth' => 'Gothic', + 'Gran' => 'Grantha', + 'Grek' => 'Greek', + 'Gujr' => 'Gujarati', + 'Guru' => 'Gurmukhi', + 'Hani' => 'Han', + 'Hang' => 'Hangul', + 'Hano' => 'Hanunoo', + 'Hebr' => 'Hebrew', + 'Hira' => 'Hiragana', + 'Armi' => 'Imperial Aramaic', + 'Inds' => 'Indus', + 'Zinh' => 'Inherited', + 'Phli' => 'Inscriptional Pahlavi', + 'Prti' => 'Inscriptional Parthian', + 'Jpan' => 'Japanese', + 'Hrkt' => 'Japanese syllabaries', + 'Java' => 'Javanese', + 'Jurc' => 'Jurchen', + 'Kthi' => 'Kaithi', + 'Knda' => 'Kannada', + 'Kana' => 'Katakana', + 'Kali' => 'Kayah Li', + 'Khar' => 'Kharoshthi', + 'Khmr' => 'Khmer', + 'Khoj' => 'Khojki', + 'Sind' => 'Khudawadi', + 'Kore' => 'Korean', + 'Kpel' => 'Kpelle', + 'Lana' => 'Lanna', + 'Laoo' => 'Lao', + 'Latn' => 'Latin', + 'Lepc' => 'Lepcha', + 'Limb' => 'Limbu', + 'Lina' => 'Linear A', + 'Linb' => 'Linear B', + 'Loma' => 'Loma', + 'Lyci' => 'Lycian', + 'Lydi' => 'Lydian', + 'Mlym' => 'Malayalam', + 'Mand' => 'Mandaean', + 'Mani' => 'Manichaean', + 'Zmth' => 'Mathematical Notation', + 'Maya' => 'Mayan hieroglyphs', + 'Mtei' => 'Meitei Mayek', + 'Mend' => 'Mende', + 'Mero' => 'Meroitic', + 'Merc' => 'Meroitic Cursive', + 'Mong' => 'Mongolian', + 'Moon' => 'Moon', + 'Mroo' => 'Mro', + 'Mymr' => 'Myanmar', + 'Nkoo' => 'N’Ko', + 'Nbat' => 'Nabataean', + 'Nkgb' => 'Naxi Geba', + 'Talu' => 'New Tai Lue', + 'Nshu' => 'Nüshu', + 'Ogam' => 'Ogham', + 'Olck' => 'Ol Chiki', + 'Cyrs' => 'Old Church Slavonic Cyrillic', + 'Hung' => 'Old Hungarian', + 'Ital' => 'Old Italic', + 'Narb' => 'Old North Arabian', + 'Perm' => 'Old Permic', + 'Xpeo' => 'Old Persian', + 'Sarb' => 'Old South Arabian', + 'Orya' => 'Oriya', + 'Orkh' => 'Orkhon', + 'Osma' => 'Osmanya', + 'Hmng' => 'Pahawh Hmong', + 'Palm' => 'Palmyrene', + 'Phag' => 'Phags-pa', + 'Phnx' => 'Phoenician', + 'Plrd' => 'Pollard Phonetic', + 'Phlp' => 'Psalter Pahlavi', + 'Rjng' => 'Rejang', + 'Roro' => 'Rongorongo', + 'Runr' => 'Runic', + 'Samr' => 'Samaritan', + 'Sara' => 'Sarati', + 'Saur' => 'Saurashtra', + 'Shrd' => 'Sharada', + 'Shaw' => 'Shavian', + 'Sgnw' => 'SignWriting', + 'Hans' => 'Simplified', + 'Sinh' => 'Sinhala', + 'Sora' => 'Sora Sompeng', + 'Xsux' => 'Sumero-Akkadian Cuneiform', + 'Sund' => 'Sundanese', + 'Sylo' => 'Syloti Nagri', + 'Zsym' => 'Symbols', + 'Syrc' => 'Syriac', + 'Tglg' => 'Tagalog', + 'Tagb' => 'Tagbanwa', + 'Tale' => 'Tai Le', + 'Tavt' => 'Tai Viet', + 'Takr' => 'Takri', + 'Taml' => 'Tamil', + 'Tang' => 'Tangut', + 'Telu' => 'Telugu', + 'Teng' => 'Tengwar', + 'Thaa' => 'Thaana', + 'Thai' => 'Thai', + 'Tibt' => 'Tibetan', + 'Tfng' => 'Tifinagh', + 'Tirh' => 'Tirhuta', + 'Hant' => 'Traditional', + 'Ugar' => 'Ugaritic', + 'Cans' => 'Unified Canadian Aboriginal Syllabics', + 'Zzzz' => 'Unknown Script', + 'Zxxx' => 'Unwritten', + 'Vaii' => 'Vai', + 'Wara' => 'Varang Kshiti', + 'Visp' => 'Visible Speech', + 'Syrj' => 'Western Syriac', + 'Wole' => 'Woleai', + 'Yiii' => 'Yi', + ), +); diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/en.php b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/en.php new file mode 100644 index 00000000..b1bcafce --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/en.php @@ -0,0 +1,305 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'Locales' => array( + 'af' => 'Afrikaans', + 'af_NA' => 'Afrikaans (Namibia)', + 'agq' => 'Aghem', + 'ak' => 'Akan', + 'sq' => 'Albanian', + 'am' => 'Amharic', + 'ar' => 'Arabic', + 'ar_DZ' => 'Arabic (Algeria)', + 'ar_IQ' => 'Arabic (Iraq)', + 'ar_JO' => 'Arabic (Jordan)', + 'ar_LB' => 'Arabic (Lebanon)', + 'ar_LY' => 'Arabic (Libya)', + 'ar_MR' => 'Arabic (Mauritania)', + 'ar_MA' => 'Arabic (Morocco)', + 'ar_PS' => 'Arabic (Palestinian Territories)', + 'ar_QA' => 'Arabic (Qatar)', + 'ar_SA' => 'Arabic (Saudi Arabia)', + 'ar_SY' => 'Arabic (Syria)', + 'ar_TN' => 'Arabic (Tunisia)', + 'ar_EH' => 'Arabic (Western Sahara)', + 'ar_YE' => 'Arabic (Yemen)', + 'hy' => 'Armenian', + 'as' => 'Assamese', + 'asa' => 'Asu', + 'az' => 'Azerbaijani', + 'az_Cyrl' => 'Azerbaijani (Cyrillic)', + 'az_Latn' => 'Azerbaijani (Latin)', + 'ksf' => 'Bafia', + 'bm' => 'Bambara', + 'bas' => 'Basaa', + 'eu' => 'Basque', + 'be' => 'Belarusian', + 'bem' => 'Bemba', + 'bez' => 'Bena', + 'bn' => 'Bengali', + 'bn_IN' => 'Bengali (India)', + 'brx' => 'Bodo', + 'bs' => 'Bosnian', + 'bs_Cyrl' => 'Bosnian (Cyrillic)', + 'br' => 'Breton', + 'bg' => 'Bulgarian', + 'my' => 'Burmese', + 'my_MM' => 'Burmese (Myanmar [Burma])', + 'ca' => 'Catalan', + 'tzm' => 'Central Atlas Tamazight', + 'tzm_Latn' => 'Central Atlas Tamazight (Latin)', + 'chr' => 'Cherokee', + 'chr_US' => 'Cherokee (United States)', + 'cgg' => 'Chiga', + 'zh' => 'Chinese', + 'zh_Hans_HK' => 'Chinese (Simplified, Hong Kong SAR China)', + 'zh_Hans_MO' => 'Chinese (Simplified, Macau SAR China)', + 'zh_Hans_SG' => 'Chinese (Simplified, Singapore)', + 'zh_Hans' => 'Chinese (Simplified)', + 'zh_Hant_HK' => 'Chinese (Traditional, Hong Kong SAR China)', + 'zh_Hant_MO' => 'Chinese (Traditional, Macau SAR China)', + 'zh_Hant' => 'Chinese (Traditional)', + 'swc' => 'Congo Swahili', + 'kw' => 'Cornish', + 'hr' => 'Croatian', + 'cs' => 'Czech', + 'da' => 'Danish', + 'dua' => 'Duala', + 'nl' => 'Dutch', + 'nl_BE' => 'Dutch (Belgium)', + 'dz' => 'Dzongkha', + 'ebu' => 'Embu', + 'en' => 'English', + 'en_AU' => 'English (Australia)', + 'en_BE' => 'English (Belgium)', + 'en_BZ' => 'English (Belize)', + 'en_BW' => 'English (Botswana)', + 'en_CA' => 'English (Canada)', + 'en_GI' => 'English (Gibraltar)', + 'en_GG' => 'English (Guernsey)', + 'en_HK' => 'English (Hong Kong SAR China)', + 'en_IN' => 'English (India)', + 'en_IE' => 'English (Ireland)', + 'en_IM' => 'English (Isle of Man)', + 'en_JM' => 'English (Jamaica)', + 'en_JE' => 'English (Jersey)', + 'en_LR' => 'English (Liberia)', + 'en_MT' => 'English (Malta)', + 'en_NA' => 'English (Namibia)', + 'en_NZ' => 'English (New Zealand)', + 'en_PK' => 'English (Pakistan)', + 'en_PH' => 'English (Philippines)', + 'en_PR' => 'English (Puerto Rico)', + 'en_SG' => 'English (Singapore)', + 'en_ZA' => 'English (South Africa)', + 'en_TT' => 'English (Trinidad and Tobago)', + 'en_GB' => 'English (United Kingdom)', + 'en_US' => 'English (United States)', + 'en_ZW' => 'English (Zimbabwe)', + 'eo' => 'Esperanto', + 'et' => 'Estonian', + 'ee' => 'Ewe', + 'ewo' => 'Ewondo', + 'fo' => 'Faroese', + 'fil' => 'Filipino', + 'fil_PH' => 'Filipino (Philippines)', + 'fi' => 'Finnish', + 'fr' => 'French', + 'fr_BE' => 'French (Belgium)', + 'fr_CA' => 'French (Canada)', + 'fr_LU' => 'French (Luxembourg)', + 'fr_CH' => 'French (Switzerland)', + 'ff' => 'Fulah', + 'gl' => 'Galician', + 'lg' => 'Ganda', + 'ka' => 'Georgian', + 'de' => 'German', + 'de_AT' => 'German (Austria)', + 'de_LI' => 'German (Liechtenstein)', + 'de_CH' => 'German (Switzerland)', + 'el' => 'Greek', + 'el_CY' => 'Greek (Cyprus)', + 'gu' => 'Gujarati', + 'guz' => 'Gusii', + 'ha' => 'Hausa', + 'ha_Latn' => 'Hausa (Latin)', + 'haw' => 'Hawaiian', + 'haw_US' => 'Hawaiian (United States)', + 'he' => 'Hebrew', + 'hi' => 'Hindi', + 'hu' => 'Hungarian', + 'is' => 'Icelandic', + 'ig' => 'Igbo', + 'id' => 'Indonesian', + 'ga' => 'Irish', + 'it' => 'Italian', + 'it_CH' => 'Italian (Switzerland)', + 'ja' => 'Japanese', + 'dyo' => 'Jola-Fonyi', + 'kea' => 'Kabuverdianu', + 'kab' => 'Kabyle', + 'kl' => 'Kalaallisut', + 'kln' => 'Kalenjin', + 'kam' => 'Kamba', + 'kn' => 'Kannada', + 'ks' => 'Kashmiri', + 'ks_Arab' => 'Kashmiri (Arabic)', + 'kk' => 'Kazakh', + 'kk_Cyrl' => 'Kazakh (Cyrillic)', + 'km' => 'Khmer', + 'ki' => 'Kikuyu', + 'rw' => 'Kinyarwanda', + 'kok' => 'Konkani', + 'ko' => 'Korean', + 'khq' => 'Koyra Chiini', + 'ses' => 'Koyraboro Senni', + 'nmg' => 'Kwasio', + 'lag' => 'Langi', + 'lo' => 'Lao', + 'lv' => 'Latvian', + 'ln' => 'Lingala', + 'lt' => 'Lithuanian', + 'lu' => 'Luba-Katanga', + 'luo' => 'Luo', + 'luy' => 'Luyia', + 'mk' => 'Macedonian', + 'jmc' => 'Machame', + 'mgh' => 'Makhuwa-Meetto', + 'kde' => 'Makonde', + 'mg' => 'Malagasy', + 'ms' => 'Malay', + 'ms_BN' => 'Malay (Brunei)', + 'ml' => 'Malayalam', + 'mt' => 'Maltese', + 'gv' => 'Manx', + 'mr' => 'Marathi', + 'mas' => 'Masai', + 'mer' => 'Meru', + 'mgo' => 'Meta\'', + 'mfe' => 'Morisyen', + 'mua' => 'Mundang', + 'naq' => 'Nama', + 'ne' => 'Nepali', + 'ne_IN' => 'Nepali (India)', + 'jgo' => 'Ngomba', + 'nd' => 'North Ndebele', + 'nb' => 'Norwegian Bokmål', + 'nn' => 'Norwegian Nynorsk', + 'nus' => 'Nuer', + 'nyn' => 'Nyankole', + 'or' => 'Oriya', + 'om' => 'Oromo', + 'ps' => 'Pashto', + 'fa' => 'Persian', + 'fa_AF' => 'Persian (Afghanistan)', + 'pl' => 'Polish', + 'pt' => 'Portuguese', + 'pt_AO' => 'Portuguese (Angola)', + 'pt_CV' => 'Portuguese (Cape Verde)', + 'pt_GW' => 'Portuguese (Guinea-Bissau)', + 'pt_MO' => 'Portuguese (Macau SAR China)', + 'pt_MZ' => 'Portuguese (Mozambique)', + 'pt_PT' => 'Portuguese (Portugal)', + 'pt_ST' => 'Portuguese (São Tomé and Príncipe)', + 'pt_TL' => 'Portuguese (Timor-Leste)', + 'pa' => 'Punjabi', + 'pa_Arab' => 'Punjabi (Arabic)', + 'pa_Guru' => 'Punjabi (Gurmukhi)', + 'ro' => 'Romanian', + 'rm' => 'Romansh', + 'rof' => 'Rombo', + 'rn' => 'Rundi', + 'ru' => 'Russian', + 'ru_UA' => 'Russian (Ukraine)', + 'rwk' => 'Rwa', + 'saq' => 'Samburu', + 'sg' => 'Sango', + 'sbp' => 'Sangu', + 'seh' => 'Sena', + 'sr' => 'Serbian', + 'sr_Cyrl_BA' => 'Serbian (Cyrillic, Bosnia and Herzegovina)', + 'sr_Cyrl' => 'Serbian (Cyrillic)', + 'sr_Latn_ME' => 'Serbian (Latin, Montenegro)', + 'sr_Latn' => 'Serbian (Latin)', + 'ksb' => 'Shambala', + 'sn' => 'Shona', + 'ii' => 'Sichuan Yi', + 'si' => 'Sinhala', + 'sk' => 'Slovak', + 'sl' => 'Slovenian', + 'xog' => 'Soga', + 'so' => 'Somali', + 'es' => 'Spanish', + 'es_AR' => 'Spanish (Argentina)', + 'es_BO' => 'Spanish (Bolivia)', + 'es_CL' => 'Spanish (Chile)', + 'es_CO' => 'Spanish (Colombia)', + 'es_CR' => 'Spanish (Costa Rica)', + 'es_CU' => 'Spanish (Cuba)', + 'es_DO' => 'Spanish (Dominican Republic)', + 'es_EC' => 'Spanish (Ecuador)', + 'es_SV' => 'Spanish (El Salvador)', + 'es_GQ' => 'Spanish (Equatorial Guinea)', + 'es_GT' => 'Spanish (Guatemala)', + 'es_HN' => 'Spanish (Honduras)', + 'es_MX' => 'Spanish (Mexico)', + 'es_NI' => 'Spanish (Nicaragua)', + 'es_PA' => 'Spanish (Panama)', + 'es_PY' => 'Spanish (Paraguay)', + 'es_PE' => 'Spanish (Peru)', + 'es_PH' => 'Spanish (Philippines)', + 'es_PR' => 'Spanish (Puerto Rico)', + 'es_US' => 'Spanish (United States)', + 'es_UY' => 'Spanish (Uruguay)', + 'es_VE' => 'Spanish (Venezuela)', + 'sw' => 'Swahili', + 'sw_KE' => 'Swahili (Kenya)', + 'sv' => 'Swedish', + 'sv_FI' => 'Swedish (Finland)', + 'gsw' => 'Swiss German', + 'shi' => 'Tachelhit', + 'shi_Latn' => 'Tachelhit (Latin)', + 'shi_Tfng' => 'Tachelhit (Tifinagh)', + 'dav' => 'Taita', + 'ta' => 'Tamil', + 'ta_MY' => 'Tamil (Malaysia)', + 'ta_SG' => 'Tamil (Singapore)', + 'twq' => 'Tasawaq', + 'te' => 'Telugu', + 'teo' => 'Teso', + 'th' => 'Thai', + 'bo' => 'Tibetan', + 'ti' => 'Tigrinya', + 'ti_ER' => 'Tigrinya (Eritrea)', + 'to' => 'Tongan', + 'tr' => 'Turkish', + 'uk' => 'Ukrainian', + 'ur' => 'Urdu', + 'ur_IN' => 'Urdu (India)', + 'uz' => 'Uzbek', + 'uz_Arab' => 'Uzbek (Arabic)', + 'uz_Cyrl' => 'Uzbek (Cyrillic)', + 'uz_Latn' => 'Uzbek (Latin)', + 'vai' => 'Vai', + 'vai_Latn_LR' => 'Vai (Latin, Liberia)', + 'vai_Latn' => 'Vai (Latin)', + 'vai_Vaii_LR' => 'Vai (Vai, Liberia)', + 'vai_Vaii' => 'Vai (Vai)', + 'vi' => 'Vietnamese', + 'vun' => 'Vunjo', + 'cy' => 'Welsh', + 'yav' => 'Yangben', + 'yo' => 'Yoruba', + 'dje' => 'Zarma', + 'zu' => 'Zulu', + ), +); diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/en.php b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/en.php new file mode 100644 index 00000000..f42d6576 --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/en.php @@ -0,0 +1,273 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'Countries' => array( + 'AF' => 'Afghanistan', + 'AX' => 'Åland Islands', + 'AL' => 'Albania', + 'DZ' => 'Algeria', + 'AS' => 'American Samoa', + 'AD' => 'Andorra', + 'AO' => 'Angola', + 'AI' => 'Anguilla', + 'AQ' => 'Antarctica', + 'AG' => 'Antigua and Barbuda', + 'AR' => 'Argentina', + 'AM' => 'Armenia', + 'AW' => 'Aruba', + 'AC' => 'Ascension Island', + 'AU' => 'Australia', + 'AT' => 'Austria', + 'AZ' => 'Azerbaijan', + 'BS' => 'Bahamas', + 'BH' => 'Bahrain', + 'BD' => 'Bangladesh', + 'BB' => 'Barbados', + 'BY' => 'Belarus', + 'BE' => 'Belgium', + 'BZ' => 'Belize', + 'BJ' => 'Benin', + 'BM' => 'Bermuda', + 'BT' => 'Bhutan', + 'BO' => 'Bolivia', + 'BA' => 'Bosnia and Herzegovina', + 'BW' => 'Botswana', + 'BV' => 'Bouvet Island', + 'BR' => 'Brazil', + 'IO' => 'British Indian Ocean Territory', + 'VG' => 'British Virgin Islands', + 'BN' => 'Brunei', + 'BG' => 'Bulgaria', + 'BF' => 'Burkina Faso', + 'BI' => 'Burundi', + 'KH' => 'Cambodia', + 'CM' => 'Cameroon', + 'CA' => 'Canada', + 'IC' => 'Canary Islands', + 'CV' => 'Cape Verde', + 'BQ' => 'Caribbean Netherlands', + 'KY' => 'Cayman Islands', + 'CF' => 'Central African Republic', + 'EA' => 'Ceuta and Melilla', + 'TD' => 'Chad', + 'CL' => 'Chile', + 'CN' => 'China', + 'CX' => 'Christmas Island', + 'CP' => 'Clipperton Island', + 'CC' => 'Cocos [Keeling] Islands', + 'CO' => 'Colombia', + 'KM' => 'Comoros', + 'CG' => 'Congo - Brazzaville', + 'CD' => 'Congo - Kinshasa', + 'CK' => 'Cook Islands', + 'CR' => 'Costa Rica', + 'CI' => 'Côte d’Ivoire', + 'HR' => 'Croatia', + 'CU' => 'Cuba', + 'CW' => 'Curaçao', + 'CY' => 'Cyprus', + 'CZ' => 'Czech Republic', + 'DK' => 'Denmark', + 'DG' => 'Diego Garcia', + 'DJ' => 'Djibouti', + 'DM' => 'Dominica', + 'DO' => 'Dominican Republic', + 'EC' => 'Ecuador', + 'EG' => 'Egypt', + 'SV' => 'El Salvador', + 'GQ' => 'Equatorial Guinea', + 'ER' => 'Eritrea', + 'EE' => 'Estonia', + 'ET' => 'Ethiopia', + 'EU' => 'European Union', + 'FK' => 'Falkland Islands', + 'FO' => 'Faroe Islands', + 'FJ' => 'Fiji', + 'FI' => 'Finland', + 'FR' => 'France', + 'GF' => 'French Guiana', + 'PF' => 'French Polynesia', + 'TF' => 'French Southern Territories', + 'GA' => 'Gabon', + 'GM' => 'Gambia', + 'GE' => 'Georgia', + 'DE' => 'Germany', + 'GH' => 'Ghana', + 'GI' => 'Gibraltar', + 'GR' => 'Greece', + 'GL' => 'Greenland', + 'GD' => 'Grenada', + 'GP' => 'Guadeloupe', + 'GU' => 'Guam', + 'GT' => 'Guatemala', + 'GG' => 'Guernsey', + 'GN' => 'Guinea', + 'GW' => 'Guinea-Bissau', + 'GY' => 'Guyana', + 'HT' => 'Haiti', + 'HM' => 'Heard Island and McDonald Islands', + 'HN' => 'Honduras', + 'HK' => 'Hong Kong SAR China', + 'HU' => 'Hungary', + 'IS' => 'Iceland', + 'IN' => 'India', + 'ID' => 'Indonesia', + 'IR' => 'Iran', + 'IQ' => 'Iraq', + 'IE' => 'Ireland', + 'IM' => 'Isle of Man', + 'IL' => 'Israel', + 'IT' => 'Italy', + 'JM' => 'Jamaica', + 'JP' => 'Japan', + 'JE' => 'Jersey', + 'JO' => 'Jordan', + 'KZ' => 'Kazakhstan', + 'KE' => 'Kenya', + 'KI' => 'Kiribati', + 'KW' => 'Kuwait', + 'KG' => 'Kyrgyzstan', + 'LA' => 'Laos', + 'LV' => 'Latvia', + 'LB' => 'Lebanon', + 'LS' => 'Lesotho', + 'LR' => 'Liberia', + 'LY' => 'Libya', + 'LI' => 'Liechtenstein', + 'LT' => 'Lithuania', + 'LU' => 'Luxembourg', + 'MO' => 'Macau SAR China', + 'MK' => 'Macedonia', + 'MG' => 'Madagascar', + 'MW' => 'Malawi', + 'MY' => 'Malaysia', + 'MV' => 'Maldives', + 'ML' => 'Mali', + 'MT' => 'Malta', + 'MH' => 'Marshall Islands', + 'MQ' => 'Martinique', + 'MR' => 'Mauritania', + 'MU' => 'Mauritius', + 'YT' => 'Mayotte', + 'MX' => 'Mexico', + 'FM' => 'Micronesia', + 'MD' => 'Moldova', + 'MC' => 'Monaco', + 'MN' => 'Mongolia', + 'ME' => 'Montenegro', + 'MS' => 'Montserrat', + 'MA' => 'Morocco', + 'MZ' => 'Mozambique', + 'MM' => 'Myanmar [Burma]', + 'NA' => 'Namibia', + 'NR' => 'Nauru', + 'NP' => 'Nepal', + 'NL' => 'Netherlands', + 'AN' => 'Netherlands Antilles', + 'NC' => 'New Caledonia', + 'NZ' => 'New Zealand', + 'NI' => 'Nicaragua', + 'NE' => 'Niger', + 'NG' => 'Nigeria', + 'NU' => 'Niue', + 'NF' => 'Norfolk Island', + 'KP' => 'North Korea', + 'MP' => 'Northern Mariana Islands', + 'NO' => 'Norway', + 'OM' => 'Oman', + 'QO' => 'Outlying Oceania', + 'PK' => 'Pakistan', + 'PW' => 'Palau', + 'PS' => 'Palestinian Territories', + 'PA' => 'Panama', + 'PG' => 'Papua New Guinea', + 'PY' => 'Paraguay', + 'PE' => 'Peru', + 'PH' => 'Philippines', + 'PN' => 'Pitcairn Islands', + 'PL' => 'Poland', + 'PT' => 'Portugal', + 'PR' => 'Puerto Rico', + 'QA' => 'Qatar', + 'RE' => 'Réunion', + 'RO' => 'Romania', + 'RU' => 'Russia', + 'RW' => 'Rwanda', + 'BL' => 'Saint Barthélemy', + 'SH' => 'Saint Helena', + 'KN' => 'Saint Kitts and Nevis', + 'LC' => 'Saint Lucia', + 'MF' => 'Saint Martin', + 'PM' => 'Saint Pierre and Miquelon', + 'VC' => 'Saint Vincent and the Grenadines', + 'WS' => 'Samoa', + 'SM' => 'San Marino', + 'ST' => 'São Tomé and Príncipe', + 'SA' => 'Saudi Arabia', + 'SN' => 'Senegal', + 'RS' => 'Serbia', + 'SC' => 'Seychelles', + 'SL' => 'Sierra Leone', + 'SG' => 'Singapore', + 'SX' => 'Sint Maarten', + 'SK' => 'Slovakia', + 'SI' => 'Slovenia', + 'SB' => 'Solomon Islands', + 'SO' => 'Somalia', + 'ZA' => 'South Africa', + 'GS' => 'South Georgia and the South Sandwich Islands', + 'KR' => 'South Korea', + 'SS' => 'South Sudan', + 'ES' => 'Spain', + 'LK' => 'Sri Lanka', + 'SD' => 'Sudan', + 'SR' => 'Suriname', + 'SJ' => 'Svalbard and Jan Mayen', + 'SZ' => 'Swaziland', + 'SE' => 'Sweden', + 'CH' => 'Switzerland', + 'SY' => 'Syria', + 'TW' => 'Taiwan', + 'TJ' => 'Tajikistan', + 'TZ' => 'Tanzania', + 'TH' => 'Thailand', + 'TL' => 'Timor-Leste', + 'TG' => 'Togo', + 'TK' => 'Tokelau', + 'TO' => 'Tonga', + 'TT' => 'Trinidad and Tobago', + 'TA' => 'Tristan da Cunha', + 'TN' => 'Tunisia', + 'TR' => 'Turkey', + 'TM' => 'Turkmenistan', + 'TC' => 'Turks and Caicos Islands', + 'TV' => 'Tuvalu', + 'UM' => 'U.S. Outlying Islands', + 'VI' => 'U.S. Virgin Islands', + 'UG' => 'Uganda', + 'UA' => 'Ukraine', + 'AE' => 'United Arab Emirates', + 'GB' => 'United Kingdom', + 'US' => 'United States', + 'UY' => 'Uruguay', + 'UZ' => 'Uzbekistan', + 'VU' => 'Vanuatu', + 'VA' => 'Vatican City', + 'VE' => 'Venezuela', + 'VN' => 'Vietnam', + 'WF' => 'Wallis and Futuna', + 'EH' => 'Western Sahara', + 'YE' => 'Yemen', + 'ZM' => 'Zambia', + 'ZW' => 'Zimbabwe', + ), +); diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/version.txt b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/version.txt new file mode 100644 index 00000000..da316782 --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/version.txt @@ -0,0 +1 @@ +50.1.2 diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Tests/IcuIntegrationTest.php b/vendor/symfony/icu/Symfony/Component/Icu/Tests/IcuIntegrationTest.php new file mode 100644 index 00000000..9fb65af3 --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/Tests/IcuIntegrationTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Icu\Tests; + +use Symfony\Component\Icu\IcuCurrencyBundle; +use Symfony\Component\Icu\IcuLanguageBundle; +use Symfony\Component\Icu\IcuLocaleBundle; +use Symfony\Component\Icu\IcuRegionBundle; +use Symfony\Component\Intl\ResourceBundle\Reader\PhpBundleReader; +use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReader; + +/** + * Verifies that the data files can actually be read. + * + * @author Bernhard Schussek + */ +class IcuIntegrationTest extends \PHPUnit_Framework_TestCase +{ + public function testCurrencyBundle() + { + $bundle = new IcuCurrencyBundle(new StructuredBundleReader(new PhpBundleReader())); + + $this->assertSame('€', $bundle->getCurrencySymbol('EUR', 'en')); + } + + public function testLanguageBundle() + { + $bundle = new IcuLanguageBundle(new StructuredBundleReader(new PhpBundleReader())); + + $this->assertSame('German', $bundle->getLanguageName('de', null, 'en')); + } + + public function testLocaleBundle() + { + $bundle = new IcuLocaleBundle(new StructuredBundleReader(new PhpBundleReader())); + + $this->assertSame('Azerbaijani', $bundle->getLocaleName('az', 'en')); + } + + public function testRegionBundle() + { + $bundle = new IcuRegionBundle(new StructuredBundleReader(new PhpBundleReader())); + + $this->assertSame('United Kingdom', $bundle->getCountryName('GB', 'en')); + } +} diff --git a/vendor/symfony/icu/Symfony/Component/Icu/composer.json b/vendor/symfony/icu/Symfony/Component/Icu/composer.json new file mode 100644 index 00000000..279430ad --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/composer.json @@ -0,0 +1,26 @@ +{ + "name": "symfony/icu", + "type": "library", + "description": "Contains an excerpt of the ICU data and classes to load it.", + "keywords": ["icu", "intl"], + "homepage": "http://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3", + "symfony/intl": "~2.3" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\Icu\\": "" } + }, + "target-dir": "Symfony/Component/Icu" +} diff --git a/vendor/symfony/icu/Symfony/Component/Icu/phpunit.xml.dist b/vendor/symfony/icu/Symfony/Component/Icu/phpunit.xml.dist new file mode 100644 index 00000000..acfe4bd9 --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/intl/Symfony/Component/Intl/.gitignore b/vendor/symfony/intl/Symfony/Component/Intl/.gitignore new file mode 100644 index 00000000..c49a5d8d --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/intl/Symfony/Component/Intl/CONTRIBUTING.md b/vendor/symfony/intl/Symfony/Component/Intl/CONTRIBUTING.md new file mode 100644 index 00000000..315c28ab --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/CONTRIBUTING.md @@ -0,0 +1,91 @@ +Contributing to the Intl component +================================== + +A very good way of contributing to the Intl component is by updating the +included data for the ICU version you have installed on your system. + +Preparation +----------- + +To prepare, you need to install the development dependencies of the component. + + $ cd /path/to/Symfony/Component/Intl + $ composer.phar install --dev + +Determining your ICU version +--------------------------- + +The ICU version installed in your PHP environment can be found by running +icu-version.php: + + $ php Resources/bin/icu-version.php + +Updating the ICU data +--------------------- + +To update the data files, run the update-icu-component.php script: + + $ php Resources/bin/update-icu-component.php + +The script needs the binaries "svn" and "make" to be available on your system. +It will download the latest version of the ICU sources for the ICU version +installed in your PHP environment. The script will then compile the "genrb" +binary and use it to compile the ICU data files to binaries. The binaries are +copied to the Resources/ directory of the Icu component found in the +vendor/symfony/icu/ directory. + +Updating the stub data +---------------------- + +In the previous step you updated the Icu component for the ICU version +installed on your system. If you are using the latest ICU version, you should +also create the stub data files which will be used by people who don't have +the intl extension installed. + +To update the stub files, run the update-stubs.php script: + + $ php Resources/bin/update-stubs.php + +The script will fail if you don't have the latest ICU version. If you want to +upgrade the ICU version, adjust the return value of the +`Intl::getIcuStubVersion()` before you run the script. + +The script creates copies of the binary resource bundles in the Icu component +and stores them in the Resources/ directory of the Intl component. The copies +are made for the locale "en" only and are stored in .php files, so that they +can be read even if the intl extension is not available. + +Creating a pull request +----------------------- + +You need to create up to two pull requests: + +* If you updated the Icu component, you need to push that change and create a + pull request in the `symfony/Icu` repository. Make sure to submit the pull + request to the correct master branch. If you updated the ICU data for version + 4.8, your pull request goes to branch `48-master`, for version 49 to + `49-master` and so on. + +* If you updated the stub files of the Intl component, you need to push that + change and create a pull request in the `symfony/symfony` repository. The + pull request should be based on the `master` branch. + +Combining .res files to a .dat-package +-------------------------------------- + +The individual *.res files can be combined into a single .dat-file. +Unfortunately, PHP's `ResourceBundle` class is currently not able to handle +.dat-files. + +Once it is, the following steps have to be followed to build the .dat-file: + +1. Package the resource bundles into a single file + + $ find . -name *.res | sed -e "s/\.\///g" > packagelist.txt + $ pkgdata -p region -T build -d . packagelist.txt + +2. Clean up + + $ rm -rf build packagelist.txt + +3. You can now move region.dat to replace the version bundled with Symfony2. diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Collator/Collator.php b/vendor/symfony/intl/Symfony/Component/Intl/Collator/Collator.php new file mode 100644 index 00000000..8c0ffc33 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/Collator/Collator.php @@ -0,0 +1,295 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Collator; + +use Symfony\Component\Intl\Exception\MethodNotImplementedException; +use Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException; +use Symfony\Component\Intl\Globals\IntlGlobals; +use Symfony\Component\Intl\Locale\Locale; + +/** + * Replacement for PHP's native {@link \Collator} class. + * + * The only methods currently supported in this class are: + * + * - {@link \__construct} + * - {@link create} + * - {@link asort} + * - {@link getErrorCode} + * - {@link getErrorMessage} + * - {@link getLocale} + * + * @author Igor Wiedler + * @author Bernhard Schussek + */ +class Collator +{ + /* Attribute constants */ + const FRENCH_COLLATION = 0; + const ALTERNATE_HANDLING = 1; + const CASE_FIRST = 2; + const CASE_LEVEL = 3; + const NORMALIZATION_MODE = 4; + const STRENGTH = 5; + const HIRAGANA_QUATERNARY_MODE = 6; + const NUMERIC_COLLATION = 7; + + /* Attribute constants values */ + const DEFAULT_VALUE = -1; + + const PRIMARY = 0; + const SECONDARY = 1; + const TERTIARY = 2; + const DEFAULT_STRENGTH = 2; + const QUATERNARY = 3; + const IDENTICAL = 15; + + const OFF = 16; + const ON = 17; + + const SHIFTED = 20; + const NON_IGNORABLE = 21; + + const LOWER_FIRST = 24; + const UPPER_FIRST = 25; + + /* Sorting options */ + const SORT_REGULAR = 0; + const SORT_NUMERIC = 2; + const SORT_STRING = 1; + + /** + * Constructor + * + * @param string $locale The locale code. The only currently supported locale is "en". + * + * @throws MethodArgumentValueNotImplementedException When $locale different than "en" is passed + */ + public function __construct($locale) + { + if ('en' != $locale) { + throw new MethodArgumentValueNotImplementedException(__METHOD__, 'locale', $locale, 'Only the locale "en" is supported'); + } + } + + /** + * Static constructor + * + * @param string $locale The locale code. The only currently supported locale is "en". + * + * @return Collator + * + * @throws MethodArgumentValueNotImplementedException When $locale different than "en" is passed + */ + public static function create($locale) + { + return new self($locale); + } + + /** + * Sort array maintaining index association + * + * @param array &$array Input array + * @param integer $sortFlag Flags for sorting, can be one of the following: + * Collator::SORT_REGULAR - compare items normally (don't change types) + * Collator::SORT_NUMERIC - compare items numerically + * Collator::SORT_STRING - compare items as strings + * + * @return Boolean True on success or false on failure + */ + public function asort(&$array, $sortFlag = self::SORT_REGULAR) + { + $intlToPlainFlagMap = array( + self::SORT_REGULAR => \SORT_REGULAR, + self::SORT_NUMERIC => \SORT_NUMERIC, + self::SORT_STRING => \SORT_STRING, + ); + + $plainSortFlag = isset($intlToPlainFlagMap[$sortFlag]) ? $intlToPlainFlagMap[$sortFlag] : self::SORT_REGULAR; + + return asort($array, $plainSortFlag); + } + + /** + * Not supported. Compare two Unicode strings + * + * @param string $str1 The first string to compare + * @param string $str2 The second string to compare + * + * @return Boolean|int Return the comparison result or false on failure: + * 1 if $str1 is greater than $str2 + * 0 if $str1 is equal than $str2 + * -1 if $str1 is less than $str2 + * + * @see http://www.php.net/manual/en/collator.compare.php + * + * @throws MethodNotImplementedException + */ + public function compare($str1, $str2) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Get a value of an integer collator attribute + * + * @param int $attr An attribute specifier, one of the attribute constants + * + * @return Boolean|int The attribute value on success or false on error + * + * @see http://www.php.net/manual/en/collator.getattribute.php + * + * @throws MethodNotImplementedException + */ + public function getAttribute($attr) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Returns collator's last error code. Always returns the U_ZERO_ERROR class constant value + * + * @return int The error code from last collator call + */ + public function getErrorCode() + { + return IntlGlobals::U_ZERO_ERROR; + } + + /** + * Returns collator's last error message. Always returns the U_ZERO_ERROR_MESSAGE class constant value + * + * @return string The error message from last collator call + */ + public function getErrorMessage() + { + return 'U_ZERO_ERROR'; + } + + /** + * Returns the collator's locale + * + * @param int $type Not supported. The locale name type to return (Locale::VALID_LOCALE or Locale::ACTUAL_LOCALE) + * + * @return string The locale used to create the collator. Currently always + * returns "en". + */ + public function getLocale($type = Locale::ACTUAL_LOCALE) + { + return 'en'; + } + + /** + * Not supported. Get sorting key for a string + * + * @param string $string The string to produce the key from + * + * @return string The collation key for $string + * + * @see http://www.php.net/manual/en/collator.getsortkey.php + * + * @throws MethodNotImplementedException + */ + public function getSortKey($string) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Get current collator's strength + * + * @return Boolean|int The current collator's strength or false on failure + * + * @see http://www.php.net/manual/en/collator.getstrength.php + * + * @throws MethodNotImplementedException + */ + public function getStrength() + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Set a collator's attribute + * + * @param int $attr An attribute specifier, one of the attribute constants + * @param int $val The attribute value, one of the attribute value constants + * + * @return Boolean True on success or false on failure + * + * @see http://www.php.net/manual/en/collator.setattribute.php + * + * @throws MethodNotImplementedException + */ + public function setAttribute($attr, $val) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Set the collator's strength + * + * @param int $strength Strength to set, possible values: + * Collator::PRIMARY + * Collator::SECONDARY + * Collator::TERTIARY + * Collator::QUATERNARY + * Collator::IDENTICAL + * Collator::DEFAULT + * + * @return Boolean True on success or false on failure + * + * @see http://www.php.net/manual/en/collator.setstrength.php + * + * @throws MethodNotImplementedException + */ + public function setStrength($strength) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Sort array using specified collator and sort keys + * + * @param array &$arr Array of strings to sort + * + * @return Boolean True on success or false on failure + * + * @see http://www.php.net/manual/en/collator.sortwithsortkeys.php + * + * @throws MethodNotImplementedException + */ + public function sortWithSortKeys(&$arr) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Sort array using specified collator + * + * @param array &$arr Array of string to sort + * @param int $sortFlag Optional sorting type, one of the following: + * Collator::SORT_REGULAR + * Collator::SORT_NUMERIC + * Collator::SORT_STRING + * + * @return Boolean True on success or false on failure + * + * @see http://www.php.net/manual/en/collator.sort.php + * + * @throws MethodNotImplementedException + */ + public function sort(&$arr, $sortFlag = self::SORT_REGULAR) + { + throw new MethodNotImplementedException(__METHOD__); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/AmPmTransformer.php b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/AmPmTransformer.php new file mode 100644 index 00000000..1a9601d8 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/AmPmTransformer.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for AM/PM markers format + * + * @author Igor Wiedler + */ +class AmPmTransformer extends Transformer +{ + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + return $dateTime->format('A'); + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + return 'AM|PM'; + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array( + 'marker' => $matched + ); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfWeekTransformer.php b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfWeekTransformer.php new file mode 100644 index 00000000..ee53a4e6 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfWeekTransformer.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for day of week format + * + * @author Igor Wiedler + */ +class DayOfWeekTransformer extends Transformer +{ + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + $dayOfWeek = $dateTime->format('l'); + switch ($length) { + case 4: + return $dayOfWeek; + case 5: + return $dayOfWeek[0]; + default: + return substr($dayOfWeek, 0, 3); + } + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + switch ($length) { + case 4: + return 'Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday'; + case 5: + return '[MTWFS]'; + default: + return 'Mon|Tue|Wed|Thu|Fri|Sat|Sun'; + } + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array(); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfYearTransformer.php b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfYearTransformer.php new file mode 100644 index 00000000..2c33888c --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfYearTransformer.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for day of year format + * + * @author Igor Wiedler + */ +class DayOfYearTransformer extends Transformer +{ + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + $dayOfYear = $dateTime->format('z') + 1; + + return $this->padLeft($dayOfYear, $length); + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + return '\d{'.$length.'}'; + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array(); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/DayTransformer.php b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/DayTransformer.php new file mode 100644 index 00000000..19d30e74 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/DayTransformer.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for day format + * + * @author Igor Wiedler + */ +class DayTransformer extends Transformer +{ + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + return $this->padLeft($dateTime->format('j'), $length); + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + return 1 === $length ? '\d{1,2}' : '\d{'.$length.'}'; + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array( + 'day' => (int) $matched, + ); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/FullTransformer.php b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/FullTransformer.php new file mode 100644 index 00000000..b89db363 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/FullTransformer.php @@ -0,0 +1,356 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\DateFormatter\DateFormat; + +use Symfony\Component\Intl\Exception\NotImplementedException; +use Symfony\Component\Intl\Globals\IntlGlobals; +use Symfony\Component\Intl\DateFormatter\DateFormat\MonthTransformer; + +/** + * Parser and formatter for date formats + * + * @author Igor Wiedler + */ +class FullTransformer +{ + private $quoteMatch = "'(?:[^']+|'')*'"; + private $implementedChars = 'MLydQqhDEaHkKmsz'; + private $notImplementedChars = 'GYuwWFgecSAZvVW'; + private $regExp; + + /** + * @var Transformer[] + */ + private $transformers; + + private $pattern; + private $timezone; + + /** + * Constructor + * + * @param string $pattern The pattern to be used to format and/or parse values + * @param string $timezone The timezone to perform the date/time calculations + */ + public function __construct($pattern, $timezone) + { + $this->pattern = $pattern; + $this->timezone = $timezone; + + $implementedCharsMatch = $this->buildCharsMatch($this->implementedChars); + $notImplementedCharsMatch = $this->buildCharsMatch($this->notImplementedChars); + $this->regExp = "/($this->quoteMatch|$implementedCharsMatch|$notImplementedCharsMatch)/"; + + $this->transformers = array( + 'M' => new MonthTransformer(), + 'L' => new MonthTransformer(), + 'y' => new YearTransformer(), + 'd' => new DayTransformer(), + 'q' => new QuarterTransformer(), + 'Q' => new QuarterTransformer(), + 'h' => new Hour1201Transformer(), + 'D' => new DayOfYearTransformer(), + 'E' => new DayOfWeekTransformer(), + 'a' => new AmPmTransformer(), + 'H' => new Hour2400Transformer(), + 'K' => new Hour1200Transformer(), + 'k' => new Hour2401Transformer(), + 'm' => new MinuteTransformer(), + 's' => new SecondTransformer(), + 'z' => new TimeZoneTransformer(), + ); + } + + /** + * Return the array of Transformer objects + * + * @return Transformer[] Associative array of Transformer objects (format char => Transformer) + */ + public function getTransformers() + { + return $this->transformers; + } + + /** + * Format a DateTime using ICU dateformat pattern + * + * @param \DateTime $dateTime A DateTime object to be used to generate the formatted value + * + * @return string The formatted value + */ + public function format(\DateTime $dateTime) + { + $that = $this; + + $formatted = preg_replace_callback($this->regExp, function($matches) use ($that, $dateTime) { + return $that->formatReplace($matches[0], $dateTime); + }, $this->pattern); + + return $formatted; + } + + /** + * Return the formatted ICU value for the matched date characters + * + * @param string $dateChars The date characters to be replaced with a formatted ICU value + * @param DateTime $dateTime A DateTime object to be used to generate the formatted value + * + * @return string The formatted value + * + * @throws NotImplementedException When it encounters a not implemented date character + */ + public function formatReplace($dateChars, $dateTime) + { + $length = strlen($dateChars); + + if ($this->isQuoteMatch($dateChars)) { + return $this->replaceQuoteMatch($dateChars); + } + + if (isset($this->transformers[$dateChars[0]])) { + $transformer = $this->transformers[$dateChars[0]]; + + return $transformer->format($dateTime, $length); + } + + // handle unimplemented characters + if (false !== strpos($this->notImplementedChars, $dateChars[0])) { + throw new NotImplementedException(sprintf("Unimplemented date character '%s' in format '%s'", $dateChars[0], $this->pattern)); + } + } + + /** + * Parse a pattern based string to a timestamp value + * + * @param \DateTime $dateTime A configured DateTime object to use to perform the date calculation + * @param string $value String to convert to a time value + * + * @return int The corresponding Unix timestamp + * + * @throws \InvalidArgumentException When the value can not be matched with pattern + */ + public function parse(\DateTime $dateTime, $value) + { + $reverseMatchingRegExp = $this->getReverseMatchingRegExp($this->pattern); + $reverseMatchingRegExp = '/^'.$reverseMatchingRegExp.'$/'; + + $options = array(); + + if (preg_match($reverseMatchingRegExp, $value, $matches)) { + $matches = $this->normalizeArray($matches); + + foreach ($this->transformers as $char => $transformer) { + if (isset($matches[$char])) { + $length = strlen($matches[$char]['pattern']); + $options = array_merge($options, $transformer->extractDateOptions($matches[$char]['value'], $length)); + } + } + + // reset error code and message + IntlGlobals::setError(IntlGlobals::U_ZERO_ERROR); + + return $this->calculateUnixTimestamp($dateTime, $options); + } + + // behave like the intl extension + IntlGlobals::setError(IntlGlobals::U_PARSE_ERROR, 'Date parsing failed'); + + return false; + } + + /** + * Retrieve a regular expression to match with a formatted value. + * + * @param string $pattern The pattern to create the reverse matching regular expression + * + * @return string The reverse matching regular expression with named captures being formed by the + * transformer index in the $transformer array + */ + public function getReverseMatchingRegExp($pattern) + { + $that = $this; + + $escapedPattern = preg_quote($pattern, '/'); + + // ICU 4.8 recognizes slash ("/") in a value to be parsed as a dash ("-") and vice-versa + // when parsing a date/time value + $escapedPattern = preg_replace('/\\\[\-|\/]/', '[\/\-]', $escapedPattern); + + $reverseMatchingRegExp = preg_replace_callback($this->regExp, function($matches) use ($that) { + $length = strlen($matches[0]); + $transformerIndex = $matches[0][0]; + + $dateChars = $matches[0]; + if ($that->isQuoteMatch($dateChars)) { + return $that->replaceQuoteMatch($dateChars); + } + + $transformers = $that->getTransformers(); + if (isset($transformers[$transformerIndex])) { + $transformer = $transformers[$transformerIndex]; + $captureName = str_repeat($transformerIndex, $length); + + return "(?P<$captureName>".$transformer->getReverseMatchingRegExp($length).')'; + } + }, $escapedPattern); + + return $reverseMatchingRegExp; + } + + /** + * Check if the first char of a string is a single quote + * + * @param string $quoteMatch The string to check + * + * @return Boolean true if matches, false otherwise + */ + public function isQuoteMatch($quoteMatch) + { + return ("'" === $quoteMatch[0]); + } + + /** + * Replaces single quotes at the start or end of a string with two single quotes + * + * @param string $quoteMatch The string to replace the quotes + * + * @return string A string with the single quotes replaced + */ + public function replaceQuoteMatch($quoteMatch) + { + if (preg_match("/^'+$/", $quoteMatch)) { + return str_replace("''", "'", $quoteMatch); + } + + return str_replace("''", "'", substr($quoteMatch, 1, -1)); + } + + /** + * Builds a chars match regular expression + * + * @param string $specialChars A string of chars to build the regular expression + * + * @return string The chars match regular expression + */ + protected function buildCharsMatch($specialChars) + { + $specialCharsArray = str_split($specialChars); + + $specialCharsMatch = implode('|', array_map(function($char) { + return $char.'+'; + }, $specialCharsArray)); + + return $specialCharsMatch; + } + + /** + * Normalize a preg_replace match array, removing the numeric keys and returning an associative array + * with the value and pattern values for the matched Transformer + * + * @param array $data + * + * @return array + */ + protected function normalizeArray(array $data) + { + $ret = array(); + + foreach ($data as $key => $value) { + if (!is_string($key)) { + continue; + } + + $ret[$key[0]] = array( + 'value' => $value, + 'pattern' => $key + ); + } + + return $ret; + } + + /** + * Calculates the Unix timestamp based on the matched values by the reverse matching regular + * expression of parse() + * + * @param \DateTime $dateTime The DateTime object to be used to calculate the timestamp + * @param array $options An array with the matched values to be used to calculate the timestamp + * + * @return Boolean|int The calculated timestamp or false if matched date is invalid + */ + protected function calculateUnixTimestamp(\DateTime $dateTime, array $options) + { + $options = $this->getDefaultValueForOptions($options); + + $year = $options['year']; + $month = $options['month']; + $day = $options['day']; + $hour = $options['hour']; + $hourInstance = $options['hourInstance']; + $minute = $options['minute']; + $second = $options['second']; + $marker = $options['marker']; + $timezone = $options['timezone']; + + // If month is false, return immediately (intl behavior) + if (false === $month) { + IntlGlobals::setError(IntlGlobals::U_PARSE_ERROR, 'Date parsing failed'); + + return false; + } + + // Normalize hour + if ($hourInstance instanceof HourTransformer) { + $hour = $hourInstance->normalizeHour($hour, $marker); + } + + // Set the timezone if different from the default one + if (null !== $timezone && $timezone !== $this->timezone) { + $dateTime->setTimezone(new \DateTimeZone($timezone)); + } + + // Normalize yy year + preg_match_all($this->regExp, $this->pattern, $matches); + if (in_array('yy', $matches[0])) { + $dateTime->setTimestamp(time()); + $year = $year > $dateTime->format('y') + 20 ? 1900 + $year : 2000 + $year; + } + + $dateTime->setDate($year, $month, $day); + $dateTime->setTime($hour, $minute, $second); + + return $dateTime->getTimestamp(); + } + + /** + * Add sensible default values for missing items in the extracted date/time options array. The values + * are base in the beginning of the Unix era + * + * @param array $options + * + * @return array + */ + private function getDefaultValueForOptions(array $options) + { + return array( + 'year' => isset($options['year']) ? $options['year'] : 1970, + 'month' => isset($options['month']) ? $options['month'] : 1, + 'day' => isset($options['day']) ? $options['day'] : 1, + 'hour' => isset($options['hour']) ? $options['hour'] : 0, + 'hourInstance' => isset($options['hourInstance']) ? $options['hourInstance'] : null, + 'minute' => isset($options['minute']) ? $options['minute'] : 0, + 'second' => isset($options['second']) ? $options['second'] : 0, + 'marker' => isset($options['marker']) ? $options['marker'] : null, + 'timezone' => isset($options['timezone']) ? $options['timezone'] : null, + ); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1200Transformer.php b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1200Transformer.php new file mode 100644 index 00000000..8c8f5ef4 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1200Transformer.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for 12 hour format (0-11) + * + * @author Igor Wiedler + */ +class Hour1200Transformer extends HourTransformer +{ + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + $hourOfDay = $dateTime->format('g'); + $hourOfDay = '12' == $hourOfDay ? '0' : $hourOfDay; + + return $this->padLeft($hourOfDay, $length); + } + + /** + * {@inheritDoc} + */ + public function normalizeHour($hour, $marker = null) + { + if ('PM' === $marker) { + $hour += 12; + } + + return $hour; + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + return '\d{1,2}'; + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array( + 'hour' => (int) $matched, + 'hourInstance' => $this + ); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1201Transformer.php b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1201Transformer.php new file mode 100644 index 00000000..a8c43702 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1201Transformer.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for 12 hour format (1-12) + * + * @author Igor Wiedler + */ +class Hour1201Transformer extends HourTransformer +{ + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + return $this->padLeft($dateTime->format('g'), $length); + } + + /** + * {@inheritDoc} + */ + public function normalizeHour($hour, $marker = null) + { + if ('PM' !== $marker && 12 === $hour) { + $hour = 0; + } elseif ('PM' === $marker && 12 !== $hour) { + // If PM and hour is not 12 (1-12), sum 12 hour + $hour += 12; + } + + return $hour; + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + return '\d{1,2}'; + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array( + 'hour' => (int) $matched, + 'hourInstance' => $this + ); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2400Transformer.php b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2400Transformer.php new file mode 100644 index 00000000..8f22da13 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2400Transformer.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for 24 hour format (0-23) + * + * @author Igor Wiedler + */ +class Hour2400Transformer extends HourTransformer +{ + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + return $this->padLeft($dateTime->format('G'), $length); + } + + /** + * {@inheritDoc} + */ + public function normalizeHour($hour, $marker = null) + { + if ('AM' == $marker) { + $hour = 0; + } elseif ('PM' == $marker) { + $hour = 12; + } + + return $hour; + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + return '\d{1,2}'; + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array( + 'hour' => (int) $matched, + 'hourInstance' => $this + ); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2401Transformer.php b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2401Transformer.php new file mode 100644 index 00000000..b0f486b9 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2401Transformer.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for 24 hour format (1-24) + * + * @author Igor Wiedler + */ +class Hour2401Transformer extends HourTransformer +{ + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + $hourOfDay = $dateTime->format('G'); + $hourOfDay = ('0' == $hourOfDay) ? '24' : $hourOfDay; + + return $this->padLeft($hourOfDay, $length); + } + + /** + * {@inheritDoc} + */ + public function normalizeHour($hour, $marker = null) + { + if ((null === $marker && 24 === $hour) || 'AM' == $marker) { + $hour = 0; + } elseif ('PM' == $marker) { + $hour = 12; + } + + return $hour; + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + return '\d{1,2}'; + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array( + 'hour' => (int) $matched, + 'hourInstance' => $this + ); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/HourTransformer.php b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/HourTransformer.php new file mode 100644 index 00000000..51097d92 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/HourTransformer.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\DateFormatter\DateFormat; + +/** + * Base class for hour transformers + * + * @author Eriksen Costa + */ +abstract class HourTransformer extends Transformer +{ + /** + * Returns a normalized hour value suitable for the hour transformer type + * + * @param int $hour The hour value + * @param string $marker An optional AM/PM marker + * + * @return int The normalized hour value + */ + abstract public function normalizeHour($hour, $marker = null); +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/MinuteTransformer.php b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/MinuteTransformer.php new file mode 100644 index 00000000..b48de292 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/MinuteTransformer.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for minute format + * + * @author Igor Wiedler + */ +class MinuteTransformer extends Transformer +{ + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + $minuteOfHour = (int) $dateTime->format('i'); + + return $this->padLeft($minuteOfHour, $length); + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + return 1 === $length ? '\d{1,2}' : '\d{'.$length.'}'; + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array( + 'minute' => (int) $matched, + ); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/MonthTransformer.php b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/MonthTransformer.php new file mode 100644 index 00000000..30c15af7 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/MonthTransformer.php @@ -0,0 +1,143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for month format + * + * @author Igor Wiedler + */ +class MonthTransformer extends Transformer +{ + /** + * @var array + */ + protected static $months = array( + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December' + ); + + /** + * Short months names (first 3 letters) + * @var array + */ + protected static $shortMonths = array(); + + /** + * Flipped $months array, $name => $index + * @var array + */ + protected static $flippedMonths = array(); + + /** + * Flipped $shortMonths array, $name => $index + * @var array + */ + protected static $flippedShortMonths = array(); + + /** + * Constructor + */ + public function __construct() + { + if (0 === count(self::$shortMonths)) { + self::$shortMonths = array_map(function($month) { + return substr($month, 0, 3); + }, self::$months); + + self::$flippedMonths = array_flip(self::$months); + self::$flippedShortMonths = array_flip(self::$shortMonths); + } + } + + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + $matchLengthMap = array( + 1 => 'n', + 2 => 'm', + 3 => 'M', + 4 => 'F', + ); + + if (isset($matchLengthMap[$length])) { + return $dateTime->format($matchLengthMap[$length]); + } + + if (5 === $length) { + return substr($dateTime->format('M'), 0, 1); + } + + return $this->padLeft($dateTime->format('m'), $length); + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + switch ($length) { + case 1: + $regExp = '\d{1,2}'; + break; + case 3: + $regExp = implode('|', self::$shortMonths); + break; + case 4: + $regExp = implode('|', self::$months); + break; + case 5: + $regExp = '[JFMASOND]'; + break; + default: + $regExp = '\d{'.$length.'}'; + break; + } + + return $regExp; + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + if (!is_numeric($matched)) { + if (3 === $length) { + $matched = self::$flippedShortMonths[$matched] + 1; + } elseif (4 === $length) { + $matched = self::$flippedMonths[$matched] + 1; + } elseif (5 === $length) { + // IntlDateFormatter::parse() always returns false for MMMMM or LLLLL + $matched = false; + } + } else { + $matched = (int) $matched; + } + + return array( + 'month' => $matched, + ); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/QuarterTransformer.php b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/QuarterTransformer.php new file mode 100644 index 00000000..8e83dc75 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/QuarterTransformer.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for quarter format + * + * @author Igor Wiedler + */ +class QuarterTransformer extends Transformer +{ + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + $month = (int) $dateTime->format('n'); + $quarter = (int) floor(($month - 1) / 3) + 1; + switch ($length) { + case 1: + case 2: + return $this->padLeft($quarter, $length); + case 3: + return 'Q'.$quarter; + default: + $map = array(1 => '1st quarter', 2 => '2nd quarter', 3 => '3rd quarter', 4 => '4th quarter'); + + return $map[$quarter]; + } + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + switch ($length) { + case 1: + case 2: + return '\d{'.$length.'}'; + case 3: + return 'Q\d'; + default: + return '(?:1st|2nd|3rd|4th) quarter'; + } + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array(); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/SecondTransformer.php b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/SecondTransformer.php new file mode 100644 index 00000000..ccbcdb41 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/SecondTransformer.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for the second format + * + * @author Igor Wiedler + */ +class SecondTransformer extends Transformer +{ + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + $secondOfMinute = (int) $dateTime->format('s'); + + return $this->padLeft($secondOfMinute, $length); + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + return 1 === $length ? '\d{1,2}' : '\d{'.$length.'}'; + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array( + 'second' => (int) $matched, + ); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/TimeZoneTransformer.php b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/TimeZoneTransformer.php new file mode 100644 index 00000000..7d74bd36 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/TimeZoneTransformer.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\DateFormatter\DateFormat; + +use Symfony\Component\Intl\Exception\NotImplementedException; + +/** + * Parser and formatter for time zone format + * + * @author Igor Wiedler + */ +class TimeZoneTransformer extends Transformer +{ + /** + * {@inheritDoc} + * + * @throws NotImplementedException When time zone is different than UTC or GMT (Etc/GMT) + */ + public function format(\DateTime $dateTime, $length) + { + $timeZone = substr($dateTime->getTimezone()->getName(), 0, 3); + + if (!in_array($timeZone, array('Etc', 'UTC'))) { + throw new NotImplementedException('Time zone different than GMT or UTC is not supported as a formatting output.'); + } + + // From ICU >= 4.8, the zero offset is not more used, example: GMT instead of GMT+00:00 + $format = (0 !== (int) $dateTime->format('O')) ? '\G\M\TP' : '\G\M\T'; + + return $dateTime->format($format); + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + return 'GMT[+-]\d{2}:?\d{2}'; + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array( + 'timezone' => self::getEtcTimeZoneId($matched) + ); + } + + /** + * Get an Etc/GMT timezone identifier for the specified timezone + * + * The PHP documentation for timezones states to not use the 'Other' time zones because them exists + * "for backwards compatibility". However all Etc/GMT time zones are in the tz database 'etcetera' file, + * which indicates they are not deprecated (neither are old names). + * + * Only GMT, Etc/Universal, Etc/Zulu, Etc/Greenwich, Etc/GMT-0, Etc/GMT+0 and Etc/GMT0 are old names and + * are linked to Etc/GMT or Etc/UTC. + * + * @param string $formattedTimeZone A GMT timezone string (GMT-03:00, e.g.) + * + * @return string A timezone identifier + * + * @see http://php.net/manual/en/timezones.others.php + * @see http://www.twinsun.com/tz/tz-link.htm + * + * @throws NotImplementedException When the GMT time zone have minutes offset different than zero + * @throws \InvalidArgumentException When the value can not be matched with pattern + */ + public static function getEtcTimeZoneId($formattedTimeZone) + { + if (preg_match('/GMT(?P[+-])(?P\d{2}):?(?P\d{2})/', $formattedTimeZone, $matches)) { + $hours = (int) $matches['hours']; + $minutes = (int) $matches['minutes']; + $signal = $matches['signal'] == '-' ? '+' : '-'; + + if (0 < $minutes) { + throw new NotImplementedException(sprintf( + 'It is not possible to use a GMT time zone with minutes offset different than zero (0). GMT time zone tried: %s.', + $formattedTimeZone + )); + } + + return 'Etc/GMT'.($hours !== 0 ? $signal.$hours : ''); + } + + throw new \InvalidArgumentException('The GMT time zone \'%s\' does not match with the supported formats GMT[+-]HH:MM or GMT[+-]HHMM.'); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Transformer.php b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Transformer.php new file mode 100644 index 00000000..0e67f8ae --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Transformer.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for date formats + * + * @author Igor Wiedler + */ +abstract class Transformer +{ + /** + * Format a value using a configured DateTime as date/time source + * + * + * @param \DateTime $dateTime A DateTime object to be used to generate the formatted value + * @param int $length The formatted value string length + * + * @return string The formatted value + */ + abstract public function format(\DateTime $dateTime, $length); + + /** + * Returns a reverse matching regular expression of a string generated by format() + * + * @param int $length The length of the value to be reverse matched + * + * @return string The reverse matching regular expression + */ + abstract public function getReverseMatchingRegExp($length); + + /** + * Extract date options from a matched value returned by the processing of the reverse matching + * regular expression + * + * @param string $matched The matched value + * @param int $length The length of the Transformer pattern string + * + * @return array An associative array + */ + abstract public function extractDateOptions($matched, $length); + + /** + * Pad a string with zeros to the left + * + * @param string $value The string to be padded + * @param int $length The length to pad + * + * @return string The padded string + */ + protected function padLeft($value, $length) + { + return str_pad($value, $length, '0', STR_PAD_LEFT); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/YearTransformer.php b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/YearTransformer.php new file mode 100644 index 00000000..c3bd699d --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/YearTransformer.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for year format + * + * @author Igor Wiedler + */ +class YearTransformer extends Transformer +{ + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + if (2 === $length) { + return $dateTime->format('y'); + } + + return $this->padLeft($dateTime->format('Y'), $length); + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + return 2 === $length ? '\d{2}' : '\d{4}'; + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array( + 'year' => (int) $matched, + ); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/IntlDateFormatter.php b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/IntlDateFormatter.php new file mode 100644 index 00000000..33a499a8 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/IntlDateFormatter.php @@ -0,0 +1,631 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\DateFormatter; + +use Symfony\Component\Intl\Globals\IntlGlobals; +use Symfony\Component\Intl\DateFormatter\DateFormat\FullTransformer; +use Symfony\Component\Intl\Exception\MethodNotImplementedException; +use Symfony\Component\Intl\Exception\MethodArgumentNotImplementedException; +use Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException; +use Symfony\Component\Intl\Locale\Locale; + +/** + * Replacement for PHP's native {@link \IntlDateFormatter} class. + * + * The only methods currently supported in this class are: + * + * - {@link __construct} + * - {@link create} + * - {@link format} + * - {@link getCalendar} + * - {@link getDateType} + * - {@link getErrorCode} + * - {@link getErrorMessage} + * - {@link getLocale} + * - {@link getPattern} + * - {@link getTimeType} + * - {@link getTimeZoneId} + * - {@link isLenient} + * - {@link parse} + * - {@link setLenient} + * - {@link setPattern} + * - {@link setTimeZoneId} + * - {@link setTimeZone} + * + * @author Igor Wiedler + * @author Bernhard Schussek + */ +class IntlDateFormatter +{ + /** + * The error code from the last operation + * + * @var integer + */ + protected $errorCode = IntlGlobals::U_ZERO_ERROR; + + /** + * The error message from the last operation + * + * @var string + */ + protected $errorMessage = 'U_ZERO_ERROR'; + + /* date/time format types */ + const NONE = -1; + const FULL = 0; + const LONG = 1; + const MEDIUM = 2; + const SHORT = 3; + + /* calendar formats */ + const TRADITIONAL = 0; + const GREGORIAN = 1; + + /** + * Patterns used to format the date when no pattern is provided + * + * @var array + */ + private $defaultDateFormats = array( + self::NONE => '', + self::FULL => 'EEEE, LLLL d, y', + self::LONG => 'LLLL d, y', + self::MEDIUM => 'LLL d, y', + self::SHORT => 'M/d/yy', + ); + + /** + * Patterns used to format the time when no pattern is provided + * + * @var array + */ + private $defaultTimeFormats = array( + self::FULL => 'h:mm:ss a zzzz', + self::LONG => 'h:mm:ss a z', + self::MEDIUM => 'h:mm:ss a', + self::SHORT => 'h:mm a', + ); + + /** + * @var int + */ + private $datetype; + + /** + * @var int + */ + private $timetype; + + /** + * @var string + */ + private $pattern; + + /** + * @var \DateTimeZone + */ + private $dateTimeZone; + + /** + * @var Boolean + */ + private $unitializedTimeZoneId = false; + + /** + * @var string + */ + private $timeZoneId; + + /** + * Constructor + * + * @param string $locale The locale code. The only currently supported locale is "en". + * @param int $datetype Type of date formatting, one of the format type constants + * @param int $timetype Type of time formatting, one of the format type constants + * @param string $timezone Timezone identifier + * @param int $calendar Calendar to use for formatting or parsing. The only currently + * supported value is IntlDateFormatter::GREGORIAN. + * @param string $pattern Optional pattern to use when formatting + * + * @see http://www.php.net/manual/en/intldateformatter.create.php + * @see http://userguide.icu-project.org/formatparse/datetime + * + * @throws MethodArgumentValueNotImplementedException When $locale different than "en" is passed + * @throws MethodArgumentValueNotImplementedException When $calendar different than GREGORIAN is passed + */ + public function __construct($locale, $datetype, $timetype, $timezone = null, $calendar = self::GREGORIAN, $pattern = null) + { + if ('en' !== $locale) { + throw new MethodArgumentValueNotImplementedException(__METHOD__, 'locale', $locale, 'Only the locale "en" is supported'); + } + + if (self::GREGORIAN !== $calendar) { + throw new MethodArgumentValueNotImplementedException(__METHOD__, 'calendar', $calendar, 'Only the GREGORIAN calendar is supported'); + } + + $this->datetype = $datetype; + $this->timetype = $timetype; + + $this->setPattern($pattern); + $this->setTimeZoneId($timezone); + } + + /** + * Static constructor + * + * @param string $locale The locale code. The only currently supported locale is "en". + * @param int $datetype Type of date formatting, one of the format type constants + * @param int $timetype Type of time formatting, one of the format type constants + * @param string $timezone Timezone identifier + * @param int $calendar Calendar to use for formatting or parsing; default is Gregorian. + * One of the calendar constants. + * @param string $pattern Optional pattern to use when formatting + * + * @return IntlDateFormatter + * + * @see http://www.php.net/manual/en/intldateformatter.create.php + * @see http://userguide.icu-project.org/formatparse/datetime + * + * @throws MethodArgumentValueNotImplementedException When $locale different than "en" is passed + * @throws MethodArgumentValueNotImplementedException When $calendar different than GREGORIAN is passed + */ + public static function create($locale, $datetype, $timetype, $timezone = null, $calendar = self::GREGORIAN, $pattern = null) + { + return new self($locale, $datetype, $timetype, $timezone, $calendar, $pattern); + } + + /** + * Format the date/time value (timestamp) as a string + * + * @param integer|\DateTime $timestamp The timestamp to format. \DateTime objects + * are supported as of PHP 5.3.4. + * + * @return string|Boolean The formatted value or false if formatting failed. + * + * @see http://www.php.net/manual/en/intldateformatter.format.php + * + * @throws MethodArgumentValueNotImplementedException If one of the formatting characters is not implemented + */ + public function format($timestamp) + { + // intl allows timestamps to be passed as arrays - we don't + if (is_array($timestamp)) { + $message = version_compare(PHP_VERSION, '5.3.4', '>=') ? + 'Only integer unix timestamps and DateTime objects are supported' : + 'Only integer unix timestamps are supported'; + + throw new MethodArgumentValueNotImplementedException(__METHOD__, 'timestamp', $timestamp, $message); + } + + // behave like the intl extension + $argumentError = null; + if (version_compare(PHP_VERSION, '5.3.4', '<') && !is_int($timestamp)) { + $argumentError = 'datefmt_format: takes either an array or an integer timestamp value '; + } elseif (version_compare(PHP_VERSION, '5.3.4', '>=') && !is_int($timestamp) && !$timestamp instanceof \DateTime) { + $argumentError = 'datefmt_format: takes either an array or an integer timestamp value or a DateTime object'; + if (version_compare(PHP_VERSION, '5.5.0-dev', '>=') && !is_int($timestamp)) { + $argumentError = sprintf('datefmt_format: string \'%s\' is not numeric, which would be required for it to be a valid date', $timestamp); + } + } + + if (null !== $argumentError) { + IntlGlobals::setError(IntlGlobals::U_ILLEGAL_ARGUMENT_ERROR, $argumentError); + $this->errorCode = IntlGlobals::getErrorCode(); + $this->errorMessage = IntlGlobals::getErrorMessage(); + + return false; + } + + // As of PHP 5.3.4, IntlDateFormatter::format() accepts DateTime instances + if (version_compare(PHP_VERSION, '5.3.4', '>=') && $timestamp instanceof \DateTime) { + $timestamp = $timestamp->getTimestamp(); + } + + $transformer = new FullTransformer($this->getPattern(), $this->getTimeZoneId()); + $formatted = $transformer->format($this->createDateTime($timestamp)); + + // behave like the intl extension + IntlGlobals::setError(IntlGlobals::U_ZERO_ERROR); + $this->errorCode = IntlGlobals::getErrorCode(); + $this->errorMessage = IntlGlobals::getErrorMessage(); + + return $formatted; + } + + /** + * Not supported. Formats an object + * + * @param object $object + * @param mixed $format + * @param string $locale + * + * @return string The formatted value + * + * @see http://www.php.net/manual/en/intldateformatter.formatobject.php + * + * @throws MethodNotImplementedException + */ + public function formatObject($object, $format = null, $locale = null) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Returns the formatter's calendar + * + * @return int The calendar being used by the formatter. Currently always returns + * IntlDateFormatter::GREGORIAN. + * + * @see http://www.php.net/manual/en/intldateformatter.getcalendar.php + */ + public function getCalendar() + { + return self::GREGORIAN; + } + + /** + * Not supported. Returns the formatter's calendar object + * + * @return object The calendar's object being used by the formatter + * + * @see http://www.php.net/manual/en/intldateformatter.getcalendarobject.php + * + * @throws MethodNotImplementedException + */ + public function getCalendarObject() + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Returns the formatter's datetype + * + * @return int The current value of the formatter + * + * @see http://www.php.net/manual/en/intldateformatter.getdatetype.php + */ + public function getDateType() + { + return $this->datetype; + } + + /** + * Returns formatter's last error code. Always returns the U_ZERO_ERROR class constant value + * + * @return int The error code from last formatter call + * + * @see http://www.php.net/manual/en/intldateformatter.geterrorcode.php + */ + public function getErrorCode() + { + return $this->errorCode; + } + + /** + * Returns formatter's last error message. Always returns the U_ZERO_ERROR_MESSAGE class constant value + * + * @return string The error message from last formatter call + * + * @see http://www.php.net/manual/en/intldateformatter.geterrormessage.php + */ + public function getErrorMessage() + { + return $this->errorMessage; + } + + /** + * Returns the formatter's locale + * + * @param int $type Not supported. The locale name type to return (Locale::VALID_LOCALE or Locale::ACTUAL_LOCALE) + * + * @return string The locale used to create the formatter. Currently always + * returns "en". + * + * @see http://www.php.net/manual/en/intldateformatter.getlocale.php + */ + public function getLocale($type = Locale::ACTUAL_LOCALE) + { + return 'en'; + } + + /** + * Returns the formatter's pattern + * + * @return string The pattern string used by the formatter + * + * @see http://www.php.net/manual/en/intldateformatter.getpattern.php + */ + public function getPattern() + { + return $this->pattern; + } + + /** + * Returns the formatter's time type + * + * @return string The time type used by the formatter + * + * @see http://www.php.net/manual/en/intldateformatter.gettimetype.php + */ + public function getTimeType() + { + return $this->timetype; + } + + /** + * Returns the formatter's timezone identifier + * + * @return string The timezone identifier used by the formatter + * + * @see http://www.php.net/manual/en/intldateformatter.gettimezoneid.php + */ + public function getTimeZoneId() + { + if (!$this->unitializedTimeZoneId) { + return $this->timeZoneId; + } + + // In PHP 5.5 default timezone depends on `date_default_timezone_get()` method + if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) { + return date_default_timezone_get(); + } + + return null; + } + + /** + * Not supported. Returns the formatter's timezone + * + * @return mixed The timezone used by the formatter + * + * @see http://www.php.net/manual/en/intldateformatter.gettimezone.php + * + * @throws MethodNotImplementedException + */ + public function getTimeZone() + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Returns whether the formatter is lenient + * + * @return Boolean Currently always returns false. + * + * @see http://www.php.net/manual/en/intldateformatter.islenient.php + * + * @throws MethodNotImplementedException + */ + public function isLenient() + { + return false; + } + + /** + * Not supported. Parse string to a field-based time value + * + * @param string $value String to convert to a time value + * @param int $position Position at which to start the parsing in $value (zero-based). + * If no error occurs before $value is consumed, $parse_pos will + * contain -1 otherwise it will contain the position at which parsing + * ended. If $parse_pos > strlen($value), the parse fails immediately. + * + * @return string Localtime compatible array of integers: contains 24 hour clock value in tm_hour field + * + * @see http://www.php.net/manual/en/intldateformatter.localtime.php + * + * @throws MethodNotImplementedException + */ + public function localtime($value, &$position = 0) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Parse string to a timestamp value + * + * @param string $value String to convert to a time value + * @param int $position Not supported. Position at which to start the parsing in $value (zero-based). + * If no error occurs before $value is consumed, $parse_pos will + * contain -1 otherwise it will contain the position at which parsing + * ended. If $parse_pos > strlen($value), the parse fails immediately. + * + * @return string Parsed value as a timestamp + * + * @see http://www.php.net/manual/en/intldateformatter.parse.php + * + * @throws MethodArgumentNotImplementedException When $position different than null, behavior not implemented + */ + public function parse($value, &$position = null) + { + // We don't calculate the position when parsing the value + if (null !== $position) { + throw new MethodArgumentNotImplementedException(__METHOD__, 'position'); + } + + $dateTime = $this->createDateTime(0); + $transformer = new FullTransformer($this->getPattern(), $this->getTimeZoneId()); + + $timestamp = $transformer->parse($dateTime, $value); + + // behave like the intl extension. FullTransformer::parse() set the proper error + $this->errorCode = IntlGlobals::getErrorCode(); + $this->errorMessage = IntlGlobals::getErrorMessage(); + + return $timestamp; + } + + /** + * Not supported. Set the formatter's calendar + * + * @param string $calendar The calendar to use. Default is IntlDateFormatter::GREGORIAN. + * + * @return Boolean true on success or false on failure + * + * @see http://www.php.net/manual/en/intldateformatter.setcalendar.php + * + * @throws MethodNotImplementedException + */ + public function setCalendar($calendar) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Set the leniency of the parser + * + * Define if the parser is strict or lenient in interpreting inputs that do not match the pattern + * exactly. Enabling lenient parsing allows the parser to accept otherwise flawed date or time + * patterns, parsing as much as possible to obtain a value. Extra space, unrecognized tokens, or + * invalid values ("February 30th") are not accepted. + * + * @param Boolean $lenient Sets whether the parser is lenient or not. Currently + * only false (strict) is supported. + * + * @return Boolean true on success or false on failure + * + * @see http://www.php.net/manual/en/intldateformatter.setlenient.php + * + * @throws MethodArgumentValueNotImplementedException When $lenient is true + */ + public function setLenient($lenient) + { + if ($lenient) { + throw new MethodArgumentValueNotImplementedException(__METHOD__, 'lenient', $lenient, 'Only the strict parser is supported'); + } + + return true; + } + + /** + * Set the formatter's pattern + * + * @param string $pattern A pattern string in conformance with the ICU IntlDateFormatter documentation + * + * @return Boolean true on success or false on failure + * + * @see http://www.php.net/manual/en/intldateformatter.setpattern.php + * @see http://userguide.icu-project.org/formatparse/datetime + */ + public function setPattern($pattern) + { + if (null === $pattern) { + $pattern = $this->getDefaultPattern(); + } + + $this->pattern = $pattern; + + return true; + } + + /** + * Set the formatter's timezone identifier + * + * @param string $timeZoneId The time zone ID string of the time zone to use. + * If NULL or the empty string, the default time zone for the + * runtime is used. + * + * @return Boolean true on success or false on failure + * + * @see http://www.php.net/manual/en/intldateformatter.settimezoneid.php + */ + public function setTimeZoneId($timeZoneId) + { + if (null === $timeZoneId) { + // In PHP 5.5 if $timeZoneId is null it fallbacks to `date_default_timezone_get()` method + if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) { + $timeZoneId = date_default_timezone_get(); + } else { + // TODO: changes were made to ext/intl in PHP 5.4.4 release that need to be investigated since it will + // use ini's date.timezone when the time zone is not provided. As a not well tested workaround, uses UTC. + // See the first two items of the commit message for more information: + // https://github.com/php/php-src/commit/eb346ef0f419b90739aadfb6cc7b7436c5b521d9 + $timeZoneId = getenv('TZ') ?: 'UTC'; + } + + $this->unitializedTimeZoneId = true; + } + + // Backup original passed time zone + $timeZone = $timeZoneId; + + // Get an Etc/GMT time zone that is accepted for \DateTimeZone + if ('GMT' !== $timeZoneId && 0 === strpos($timeZoneId, 'GMT')) { + try { + $timeZoneId = DateFormat\TimeZoneTransformer::getEtcTimeZoneId($timeZoneId); + } catch (\InvalidArgumentException $e) { + // Does nothing, will fallback to UTC + } + } + + try { + $this->dateTimeZone = new \DateTimeZone($timeZoneId); + } catch (\Exception $e) { + $this->dateTimeZone = new \DateTimeZone('UTC'); + } + + $this->timeZoneId = $timeZone; + + return true; + } + + /** + * This method was added in PHP 5.5 as replacement for `setTimeZoneId()` + * + * @param mixed $timeZone + * + * @return Boolean true on success or false on failure + * + * @see http://www.php.net/manual/en/intldateformatter.settimezone.php + */ + public function setTimeZone($timeZone) + { + return $this->setTimeZoneId($timeZone); + } + + /** + * Create and returns a DateTime object with the specified timestamp and with the + * current time zone + * + * @param int $timestamp + * + * @return \DateTime + */ + protected function createDateTime($timestamp) + { + $dateTime = new \DateTime(); + $dateTime->setTimestamp($timestamp); + $dateTime->setTimezone($this->dateTimeZone); + + return $dateTime; + } + + /** + * Returns a pattern string based in the datetype and timetype values + * + * @return string + */ + protected function getDefaultPattern() + { + $patternParts = array(); + if (self::NONE !== $this->datetype) { + $patternParts[] = $this->defaultDateFormats[$this->datetype]; + } + if (self::NONE !== $this->timetype) { + $patternParts[] = $this->defaultTimeFormats[$this->timetype]; + } + $pattern = implode(' ', $patternParts); + + return $pattern; + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Exception/BadMethodCallException.php b/vendor/symfony/intl/Symfony/Component/Intl/Exception/BadMethodCallException.php new file mode 100644 index 00000000..ca79729f --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/Exception/BadMethodCallException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Exception; + +/** + * Base BadMethodCallException for the Intl component. + * + * @author Bernhard Schussek + */ +class BadMethodCallException extends \BadMethodCallException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Exception/ExceptionInterface.php b/vendor/symfony/intl/Symfony/Component/Intl/Exception/ExceptionInterface.php new file mode 100644 index 00000000..4fc076ca --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/Exception/ExceptionInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Exception; + +/** + * Base ExceptionInterface for the Intl component. + * + * @author Bernhard Schussek + */ +interface ExceptionInterface +{ +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Exception/InvalidArgumentException.php b/vendor/symfony/intl/Symfony/Component/Intl/Exception/InvalidArgumentException.php new file mode 100644 index 00000000..10f69ee3 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/Exception/InvalidArgumentException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Exception; + +/** + * InvalidArgumentException for the Intl component. + * + * @author Bernhard Schussek + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Exception/MethodArgumentNotImplementedException.php b/vendor/symfony/intl/Symfony/Component/Intl/Exception/MethodArgumentNotImplementedException.php new file mode 100644 index 00000000..570609d0 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/Exception/MethodArgumentNotImplementedException.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Exception; + +use Symfony\Component\Intl\Exception\NotImplementedException; + +/** + * @author Eriksen Costa + */ +class MethodArgumentNotImplementedException extends NotImplementedException +{ + /** + * Constructor + * + * @param string $methodName The method name that raised the exception + * @param string $argName The argument name that is not implemented + */ + public function __construct($methodName, $argName) + { + $message = sprintf('The %s() method\'s argument $%s behavior is not implemented.', $methodName, $argName); + parent::__construct($message); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Exception/MethodArgumentValueNotImplementedException.php b/vendor/symfony/intl/Symfony/Component/Intl/Exception/MethodArgumentValueNotImplementedException.php new file mode 100644 index 00000000..76e3f634 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/Exception/MethodArgumentValueNotImplementedException.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Exception; + +use Symfony\Component\Intl\Exception\NotImplementedException; + +/** + * @author Eriksen Costa + */ +class MethodArgumentValueNotImplementedException extends NotImplementedException +{ + /** + * Constructor + * + * @param string $methodName The method name that raised the exception + * @param string $argName The argument name + * @param string $argValue The argument value that is not implemented + * @param string $additionalMessage An optional additional message to append to the exception message + */ + public function __construct($methodName, $argName, $argValue, $additionalMessage = '') + { + $message = sprintf( + 'The %s() method\'s argument $%s value %s behavior is not implemented.%s', + $methodName, + $argName, + var_export($argValue, true), + $additionalMessage !== '' ? ' '.$additionalMessage.'. ' : '' + ); + + parent::__construct($message); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Exception/MethodNotImplementedException.php b/vendor/symfony/intl/Symfony/Component/Intl/Exception/MethodNotImplementedException.php new file mode 100644 index 00000000..d8a0e90f --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/Exception/MethodNotImplementedException.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Exception; + +/** + * @author Eriksen Costa + */ +class MethodNotImplementedException extends NotImplementedException +{ + /** + * Constructor + * + * @param string $methodName The name of the method + */ + public function __construct($methodName) + { + parent::__construct(sprintf('The %s() is not implemented.', $methodName)); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Exception/NotImplementedException.php b/vendor/symfony/intl/Symfony/Component/Intl/Exception/NotImplementedException.php new file mode 100644 index 00000000..1f3ba46b --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/Exception/NotImplementedException.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Exception; + +/** + * Base exception class for not implemented behaviors of the intl extension in the Locale component. + * + * @author Eriksen Costa + */ +class NotImplementedException extends RuntimeException +{ + const INTL_INSTALL_MESSAGE = 'Please install the "intl" extension for full localization capabilities.'; + + /** + * Constructor + * + * @param string $message The exception message. A note to install the intl extension is appended to this string + */ + public function __construct($message) + { + parent::__construct($message.' '.self::INTL_INSTALL_MESSAGE); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Exception/OutOfBoundsException.php b/vendor/symfony/intl/Symfony/Component/Intl/Exception/OutOfBoundsException.php new file mode 100644 index 00000000..2727141d --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/Exception/OutOfBoundsException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Exception; + +/** + * Base OutOfBoundsException for the Intl component. + * + * @author Bernhard Schussek + */ +class OutOfBoundsException extends \OutOfBoundsException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Exception/RuntimeException.php b/vendor/symfony/intl/Symfony/Component/Intl/Exception/RuntimeException.php new file mode 100644 index 00000000..98937142 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/Exception/RuntimeException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Exception; + +/** + * RuntimeException for the Intl component. + * + * @author Bernhard Schussek + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Globals/IntlGlobals.php b/vendor/symfony/intl/Symfony/Component/Intl/Globals/IntlGlobals.php new file mode 100644 index 00000000..6da00011 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/Globals/IntlGlobals.php @@ -0,0 +1,137 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Globals; + +/** + * Provides fake static versions of the global functions in the intl extension + * + * @author Bernhard Schussek + */ +abstract class IntlGlobals +{ + /** + * Indicates that no error occurred + * + * @var integer + */ + const U_ZERO_ERROR = 0; + + /** + * Indicates that an invalid argument was passed + * + * @var integer + */ + const U_ILLEGAL_ARGUMENT_ERROR = 1; + + /** + * Indicates that the parse() operation failed + * + * @var integer + */ + const U_PARSE_ERROR = 9; + + /** + * All known error codes + * + * @var array + */ + private static $errorCodes = array( + self::U_ZERO_ERROR => 'U_ZERO_ERROR', + self::U_ILLEGAL_ARGUMENT_ERROR => 'U_ILLEGAL_ARGUMENT_ERROR', + self::U_PARSE_ERROR => 'U_PARSE_ERROR', + ); + + /** + * The error code of the last operation + * + * @var integer + */ + private static $errorCode = self::U_ZERO_ERROR; + + /** + * The error code of the last operation + * + * @var integer + */ + private static $errorMessage = 'U_ZERO_ERROR'; + + /** + * Returns whether the error code indicates a failure + * + * @param integer $errorCode The error code returned by IntlGlobals::getErrorCode() + * + * @return Boolean + */ + public static function isFailure($errorCode) + { + return isset(self::$errorCodes[$errorCode]) + && $errorCode > self::U_ZERO_ERROR; + } + + /** + * Returns the error code of the last operation + * + * Returns IntlGlobals::U_ZERO_ERROR if no error occurred. + * + * @return integer + */ + public static function getErrorCode() + { + return self::$errorCode; + } + + /** + * Returns the error message of the last operation + * + * Returns "U_ZERO_ERROR" if no error occurred. + * + * @return string + */ + public static function getErrorMessage() + { + return self::$errorMessage; + } + + /** + * Returns the symbolic name for a given error code + * + * @param integer $code The error code returned by IntlGlobals::getErrorCode() + * + * @return string + */ + public static function getErrorName($code) + { + if (isset(self::$errorCodes[$code])) { + return self::$errorCodes[$code]; + } + + return '[BOGUS UErrorCode]'; + } + + /** + * Sets the current error + * + * @param integer $code One of the error constants in this class + * @param string $message The ICU class error message + * + * @throws \InvalidArgumentException If the code is not one of the error constants in this class + */ + public static function setError($code, $message = '') + { + if (!isset(self::$errorCodes[$code])) { + throw new \InvalidArgumentException(sprintf('No such error code: "%s"', $code)); + } + + self::$errorMessage = $message ? sprintf('%s: %s', $message, self::$errorCodes[$code]) : self::$errorCodes[$code]; + self::$errorCode = $code; + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Intl.php b/vendor/symfony/intl/Symfony/Component/Intl/Intl.php new file mode 100644 index 00000000..c13899a4 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/Intl.php @@ -0,0 +1,211 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl; + +use Symfony\Component\Icu\IcuCurrencyBundle; +use Symfony\Component\Icu\IcuData; +use Symfony\Component\Icu\IcuLanguageBundle; +use Symfony\Component\Icu\IcuLocaleBundle; +use Symfony\Component\Icu\IcuRegionBundle; +use Symfony\Component\Intl\ResourceBundle\Reader\BufferedBundleReader; +use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReader; + +/** + * Gives access to internationalization data. + * + * @author Bernhard Schussek + */ +class Intl +{ + /** + * The number of resource bundles to buffer. Loading the same resource + * bundle for n locales takes up n spots in the buffer. + */ + const BUFFER_SIZE = 10; + + /** + * @var ResourceBundle\CurrencyBundleInterface + */ + private static $currencyBundle; + + /** + * @var ResourceBundle\LanguageBundleInterface + */ + private static $languageBundle; + + /** + * @var ResourceBundle\LocaleBundleInterface + */ + private static $localeBundle; + + /** + * @var ResourceBundle\RegionBundleInterface + */ + private static $regionBundle; + + /** + * @var string|Boolean|null + */ + private static $icuVersion = false; + + /** + * @var string + */ + private static $icuDataVersion = false; + + /** + * @var ResourceBundle\Reader\StructuredBundleReaderInterface + */ + private static $bundleReader; + + /** + * Returns whether the intl extension is installed. + * + * @return Boolean Returns true if the intl extension is installed, false otherwise. + */ + public static function isExtensionLoaded() + { + return class_exists('\ResourceBundle'); + } + + /** + * Returns the bundle containing currency information. + * + * @return ResourceBundle\CurrencyBundleInterface The currency resource bundle. + */ + public static function getCurrencyBundle() + { + if (null === self::$currencyBundle) { + self::$currencyBundle = new IcuCurrencyBundle(self::getBundleReader()); + } + + return self::$currencyBundle; + } + + /** + * Returns the bundle containing language information. + * + * @return ResourceBundle\LanguageBundleInterface The language resource bundle. + */ + public static function getLanguageBundle() + { + if (null === self::$languageBundle) { + self::$languageBundle = new IcuLanguageBundle(self::getBundleReader()); + } + + return self::$languageBundle; + } + + /** + * Returns the bundle containing locale information. + * + * @return ResourceBundle\LocaleBundleInterface The locale resource bundle. + */ + public static function getLocaleBundle() + { + if (null === self::$localeBundle) { + self::$localeBundle = new IcuLocaleBundle(self::getBundleReader()); + } + + return self::$localeBundle; + } + + /** + * Returns the bundle containing region information. + * + * @return ResourceBundle\RegionBundleInterface The region resource bundle. + */ + public static function getRegionBundle() + { + if (null === self::$regionBundle) { + self::$regionBundle = new IcuRegionBundle(self::getBundleReader()); + } + + return self::$regionBundle; + } + + /** + * Returns the version of the installed ICU library. + * + * @return null|string The ICU version or NULL if it could not be determined. + */ + public static function getIcuVersion() + { + if (false === self::$icuVersion) { + if (!self::isExtensionLoaded()) { + self::$icuVersion = self::getIcuStubVersion(); + } elseif (defined('INTL_ICU_VERSION')) { + self::$icuVersion = INTL_ICU_VERSION; + } else { + try { + $reflector = new \ReflectionExtension('intl'); + ob_start(); + $reflector->info(); + $output = strip_tags(ob_get_clean()); + preg_match('/^ICU version (?:=>)?(.*)$/m', $output, $matches); + + self::$icuVersion = trim($matches[1]); + } catch (\ReflectionException $e) { + self::$icuVersion = null; + } + } + } + + return self::$icuVersion; + } + + /** + * Returns the version of the installed ICU data. + * + * @return string The version of the installed ICU data. + */ + public static function getIcuDataVersion() + { + if (false === self::$icuDataVersion) { + self::$icuDataVersion = IcuData::getVersion(); + } + + return self::$icuDataVersion; + } + + /** + * Returns the ICU version that the stub classes mimic. + * + * @return string The ICU version of the stub classes. + */ + public static function getIcuStubVersion() + { + return '50.1.2'; + } + + /** + * Returns a resource bundle reader for .php resource bundle files. + * + * @return ResourceBundle\Reader\StructuredBundleReaderInterface The resource reader. + */ + private static function getBundleReader() + { + if (null === self::$bundleReader) { + self::$bundleReader = new StructuredBundleReader(new BufferedBundleReader( + IcuData::getBundleReader(), + self::BUFFER_SIZE + )); + } + + return self::$bundleReader; + } + + /** + * This class must not be instantiated. + */ + private function __construct() {} +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/LICENSE b/vendor/symfony/intl/Symfony/Component/Intl/LICENSE new file mode 100644 index 00000000..88a57f8d --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2013 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. diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Locale/Locale.php b/vendor/symfony/intl/Symfony/Component/Intl/Locale/Locale.php new file mode 100644 index 00000000..cca4e9e4 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/Locale/Locale.php @@ -0,0 +1,317 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Locale; + +use Symfony\Component\Intl\Exception\MethodNotImplementedException; + +/** + * Replacement for PHP's native {@link \Locale} class. + * + * The only method supported in this class is {@link getDefault}. This method + * will always return "en". All other methods will throw an exception when used. + * + * @author Eriksen Costa + * @author Bernhard Schussek + */ +class Locale +{ + const DEFAULT_LOCALE = null; + + /* Locale method constants */ + const ACTUAL_LOCALE = 0; + const VALID_LOCALE = 1; + + /* Language tags constants */ + const LANG_TAG = 'language'; + const EXTLANG_TAG = 'extlang'; + const SCRIPT_TAG = 'script'; + const REGION_TAG = 'region'; + const VARIANT_TAG = 'variant'; + const GRANDFATHERED_LANG_TAG = 'grandfathered'; + const PRIVATE_TAG = 'private'; + + /** + * Not supported. Returns the best available locale based on HTTP "Accept-Language" header according to RFC 2616 + * + * @param string $header The string containing the "Accept-Language" header value + * + * @return string The corresponding locale code + * + * @see http://www.php.net/manual/en/locale.acceptfromhttp.php + * + * @throws MethodNotImplementedException + */ + public static function acceptFromHttp($header) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns a correctly ordered and delimited locale code + * + * @param array $subtags A keyed array where the keys identify the particular locale code subtag + * + * @return string The corresponding locale code + * + * @see http://www.php.net/manual/en/locale.composelocale.php + * + * @throws MethodNotImplementedException + */ + public static function composeLocale(array $subtags) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Checks if a language tag filter matches with locale + * + * @param string $langtag The language tag to check + * @param string $locale The language range to check against + * @param Boolean $canonicalize + * + * @return string The corresponding locale code + * + * @see http://www.php.net/manual/en/locale.filtermatches.php + * + * @throws MethodNotImplementedException + */ + public static function filterMatches($langtag, $locale, $canonicalize = false) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns the variants for the input locale + * + * @param string $locale The locale to extract the variants from + * + * @return array The locale variants + * + * @see http://www.php.net/manual/en/locale.getallvariants.php + * + * @throws MethodNotImplementedException + */ + public static function getAllVariants($locale) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Returns the default locale + * + * @return string The default locale code. Always returns 'en' + * + * @see http://www.php.net/manual/en/locale.getdefault.php + */ + public static function getDefault() + { + return 'en'; + } + + /** + * Not supported. Returns the localized display name for the locale language + * + * @param string $locale The locale code to return the display language from + * @param string $inLocale Optional format locale code to use to display the language name + * + * @return string The localized language display name + * + * @see http://www.php.net/manual/en/locale.getdisplaylanguage.php + * + * @throws MethodNotImplementedException + */ + public static function getDisplayLanguage($locale, $inLocale = null) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns the localized display name for the locale + * + * @param string $locale The locale code to return the display locale name from + * @param string $inLocale Optional format locale code to use to display the locale name + * + * @return string The localized locale display name + * + * @see http://www.php.net/manual/en/locale.getdisplayname.php + * + * @throws MethodNotImplementedException + */ + public static function getDisplayName($locale, $inLocale = null) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns the localized display name for the locale region + * + * @param string $locale The locale code to return the display region from + * @param string $inLocale Optional format locale code to use to display the region name + * + * @return string The localized region display name + * + * @see http://www.php.net/manual/en/locale.getdisplayregion.php + * + * @throws MethodNotImplementedException + */ + public static function getDisplayRegion($locale, $inLocale = null) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns the localized display name for the locale script + * + * @param string $locale The locale code to return the display script from + * @param string $inLocale Optional format locale code to use to display the script name + * + * @return string The localized script display name + * + * @see http://www.php.net/manual/en/locale.getdisplayscript.php + * + * @throws MethodNotImplementedException + */ + public static function getDisplayScript($locale, $inLocale = null) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns the localized display name for the locale variant + * + * @param string $locale The locale code to return the display variant from + * @param string $inLocale Optional format locale code to use to display the variant name + * + * @return string The localized variant display name + * + * @see http://www.php.net/manual/en/locale.getdisplayvariant.php + * + * @throws MethodNotImplementedException + */ + public static function getDisplayVariant($locale, $inLocale = null) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns the keywords for the locale + * + * @param string $locale The locale code to extract the keywords from + * + * @return array Associative array with the extracted variants + * + * @see http://www.php.net/manual/en/locale.getkeywords.php + * + * @throws MethodNotImplementedException + */ + public static function getKeywords($locale) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns the primary language for the locale + * + * @param string $locale The locale code to extract the language code from + * + * @return string|null The extracted language code or null in case of error + * + * @see http://www.php.net/manual/en/locale.getprimarylanguage.php + * + * @throws MethodNotImplementedException + */ + public static function getPrimaryLanguage($locale) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns the region for the locale + * + * @param string $locale The locale code to extract the region code from + * + * @return string|null The extracted region code or null if not present + * + * @see http://www.php.net/manual/en/locale.getregion.php + * + * @throws MethodNotImplementedException + */ + public static function getRegion($locale) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns the script for the locale + * + * @param string $locale The locale code to extract the script code from + * + * @return string|null The extracted script code or null if not present + * + * @see http://www.php.net/manual/en/locale.getscript.php + * + * @throws MethodNotImplementedException + */ + public static function getScript($locale) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns the closest language tag for the locale + * + * @param array $langtag A list of the language tags to compare to locale + * @param string $locale The locale to use as the language range when matching + * @param Boolean $canonicalize If true, the arguments will be converted to canonical form before matching + * @param string $default The locale to use if no match is found + * + * @see http://www.php.net/manual/en/locale.lookup.php + * + * @throws MethodNotImplementedException + */ + public static function lookup(array $langtag, $locale, $canonicalize = false, $default = null) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns an associative array of locale identifier subtags + * + * @param string $locale The locale code to extract the subtag array from + * + * @return array Associative array with the extracted subtags + * + * @see http://www.php.net/manual/en/locale.parselocale.php + * + * @throws MethodNotImplementedException + */ + public static function parseLocale($locale) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Sets the default runtime locale + * + * @param string $locale The locale code + * + * @return Boolean true on success or false on failure + * + * @see http://www.php.net/manual/en/locale.parselocale.php + * + * @throws MethodNotImplementedException + */ + public static function setDefault($locale) + { + throw new MethodNotImplementedException(__METHOD__); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php b/vendor/symfony/intl/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php new file mode 100644 index 00000000..0e5652e2 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php @@ -0,0 +1,891 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\NumberFormatter; + +use Symfony\Component\Intl\Exception\NotImplementedException; +use Symfony\Component\Intl\Exception\MethodNotImplementedException; +use Symfony\Component\Intl\Exception\MethodArgumentNotImplementedException; +use Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException; +use Symfony\Component\Intl\Globals\IntlGlobals; +use Symfony\Component\Intl\Intl; +use Symfony\Component\Intl\Locale\Locale; + +/** + * Replacement for PHP's native {@link \NumberFormatter} class. + * + * The only methods currently supported in this class are: + * + * - {@link __construct} + * - {@link create} + * - {@link formatCurrency} + * - {@link format} + * - {@link getAttribute} + * - {@link getErrorCode} + * - {@link getErrorMessage} + * - {@link getLocale} + * - {@link parse} + * - {@link setAttribute} + * + * @author Eriksen Costa + * @author Bernhard Schussek + */ +class NumberFormatter +{ + /* Format style constants */ + const PATTERN_DECIMAL = 0; + const DECIMAL = 1; + const CURRENCY = 2; + const PERCENT = 3; + const SCIENTIFIC = 4; + const SPELLOUT = 5; + const ORDINAL = 6; + const DURATION = 7; + const PATTERN_RULEBASED = 9; + const IGNORE = 0; + const DEFAULT_STYLE = 1; + + /* Format type constants */ + const TYPE_DEFAULT = 0; + const TYPE_INT32 = 1; + const TYPE_INT64 = 2; + const TYPE_DOUBLE = 3; + const TYPE_CURRENCY = 4; + + /* Numeric attribute constants */ + const PARSE_INT_ONLY = 0; + const GROUPING_USED = 1; + const DECIMAL_ALWAYS_SHOWN = 2; + const MAX_INTEGER_DIGITS = 3; + const MIN_INTEGER_DIGITS = 4; + const INTEGER_DIGITS = 5; + const MAX_FRACTION_DIGITS = 6; + const MIN_FRACTION_DIGITS = 7; + const FRACTION_DIGITS = 8; + const MULTIPLIER = 9; + const GROUPING_SIZE = 10; + const ROUNDING_MODE = 11; + const ROUNDING_INCREMENT = 12; + const FORMAT_WIDTH = 13; + const PADDING_POSITION = 14; + const SECONDARY_GROUPING_SIZE = 15; + const SIGNIFICANT_DIGITS_USED = 16; + const MIN_SIGNIFICANT_DIGITS = 17; + const MAX_SIGNIFICANT_DIGITS = 18; + const LENIENT_PARSE = 19; + + /* Text attribute constants */ + const POSITIVE_PREFIX = 0; + const POSITIVE_SUFFIX = 1; + const NEGATIVE_PREFIX = 2; + const NEGATIVE_SUFFIX = 3; + const PADDING_CHARACTER = 4; + const CURRENCY_CODE = 5; + const DEFAULT_RULESET = 6; + const PUBLIC_RULESETS = 7; + + /* Format symbol constants */ + const DECIMAL_SEPARATOR_SYMBOL = 0; + const GROUPING_SEPARATOR_SYMBOL = 1; + const PATTERN_SEPARATOR_SYMBOL = 2; + const PERCENT_SYMBOL = 3; + const ZERO_DIGIT_SYMBOL = 4; + const DIGIT_SYMBOL = 5; + const MINUS_SIGN_SYMBOL = 6; + const PLUS_SIGN_SYMBOL = 7; + const CURRENCY_SYMBOL = 8; + const INTL_CURRENCY_SYMBOL = 9; + const MONETARY_SEPARATOR_SYMBOL = 10; + const EXPONENTIAL_SYMBOL = 11; + const PERMILL_SYMBOL = 12; + const PAD_ESCAPE_SYMBOL = 13; + const INFINITY_SYMBOL = 14; + const NAN_SYMBOL = 15; + const SIGNIFICANT_DIGIT_SYMBOL = 16; + const MONETARY_GROUPING_SEPARATOR_SYMBOL = 17; + + /* Rounding mode values used by NumberFormatter::setAttribute() with NumberFormatter::ROUNDING_MODE attribute */ + const ROUND_CEILING = 0; + const ROUND_FLOOR = 1; + const ROUND_DOWN = 2; + const ROUND_UP = 3; + const ROUND_HALFEVEN = 4; + const ROUND_HALFDOWN = 5; + const ROUND_HALFUP = 6; + + /* Pad position values used by NumberFormatter::setAttribute() with NumberFormatter::PADDING_POSITION attribute */ + const PAD_BEFORE_PREFIX = 0; + const PAD_AFTER_PREFIX = 1; + const PAD_BEFORE_SUFFIX = 2; + const PAD_AFTER_SUFFIX = 3; + + /** + * The error code from the last operation + * + * @var integer + */ + protected $errorCode = IntlGlobals::U_ZERO_ERROR; + + /** + * The error message from the last operation + * + * @var string + */ + protected $errorMessage = 'U_ZERO_ERROR'; + + /** + * @var int + */ + private $style; + + /** + * Default values for the en locale + * + * @var array + */ + private $attributes = array( + self::FRACTION_DIGITS => 0, + self::GROUPING_USED => 1, + self::ROUNDING_MODE => self::ROUND_HALFEVEN + ); + + /** + * Holds the initialized attributes code + * + * @var array + */ + private $initializedAttributes = array(); + + /** + * The supported styles to the constructor $styles argument + * + * @var array + */ + private static $supportedStyles = array( + 'CURRENCY' => self::CURRENCY, + 'DECIMAL' => self::DECIMAL + ); + + /** + * Supported attributes to the setAttribute() $attr argument + * + * @var array + */ + private static $supportedAttributes = array( + 'FRACTION_DIGITS' => self::FRACTION_DIGITS, + 'GROUPING_USED' => self::GROUPING_USED, + 'ROUNDING_MODE' => self::ROUNDING_MODE + ); + + /** + * The available rounding modes for setAttribute() usage with + * NumberFormatter::ROUNDING_MODE. NumberFormatter::ROUND_DOWN + * and NumberFormatter::ROUND_UP does not have a PHP only equivalent + * + * @var array + */ + private static $roundingModes = array( + 'ROUND_HALFEVEN' => self::ROUND_HALFEVEN, + 'ROUND_HALFDOWN' => self::ROUND_HALFDOWN, + 'ROUND_HALFUP' => self::ROUND_HALFUP + ); + + /** + * The mapping between NumberFormatter rounding modes to the available + * modes in PHP's round() function. + * + * @see http://www.php.net/manual/en/function.round.php + * + * @var array + */ + private static $phpRoundingMap = array( + self::ROUND_HALFDOWN => \PHP_ROUND_HALF_DOWN, + self::ROUND_HALFEVEN => \PHP_ROUND_HALF_EVEN, + self::ROUND_HALFUP => \PHP_ROUND_HALF_UP + ); + + /** + * The maximum values of the integer type in 32 bit platforms. + * + * @var array + */ + private static $int32Range = array( + 'positive' => 2147483647, + 'negative' => -2147483648 + ); + + /** + * The maximum values of the integer type in 64 bit platforms. + * + * @var array + */ + private static $int64Range = array( + 'positive' => 9223372036854775807, + 'negative' => -9223372036854775808 + ); + + /** + * Constructor. + * + * @param string $locale The locale code. The only currently supported locale is "en". + * @param int $style Style of the formatting, one of the format style constants. + * The only supported styles are NumberFormatter::DECIMAL + * and NumberFormatter::CURRENCY. + * @param string $pattern Not supported. A pattern string in case $style is NumberFormat::PATTERN_DECIMAL or + * NumberFormat::PATTERN_RULEBASED. It must conform to the syntax + * described in the ICU DecimalFormat or ICU RuleBasedNumberFormat documentation + * + * @see http://www.php.net/manual/en/numberformatter.create.php + * @see http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details + * @see http://www.icu-project.org/apiref/icu4c/classRuleBasedNumberFormat.html#_details + * + * @throws MethodArgumentValueNotImplementedException When $locale different than "en" is passed + * @throws MethodArgumentValueNotImplementedException When the $style is not supported + * @throws MethodArgumentNotImplementedException When the pattern value is different than null + */ + public function __construct($locale = 'en', $style = null, $pattern = null) + { + if ('en' != $locale) { + throw new MethodArgumentValueNotImplementedException(__METHOD__, 'locale', $locale, 'Only the locale "en" is supported'); + } + + if (!in_array($style, self::$supportedStyles)) { + $message = sprintf('The available styles are: %s.', implode(', ', array_keys(self::$supportedStyles))); + throw new MethodArgumentValueNotImplementedException(__METHOD__, 'style', $style, $message); + } + + if (null !== $pattern) { + throw new MethodArgumentNotImplementedException(__METHOD__, 'pattern'); + } + + $this->style = $style; + } + + /** + * Static constructor. + * + * @param string $locale The locale code. The only supported locale is "en". + * @param int $style Style of the formatting, one of the format style constants. + * The only currently supported styles are NumberFormatter::DECIMAL + * and NumberFormatter::CURRENCY. + * @param string $pattern Not supported. A pattern string in case $style is NumberFormat::PATTERN_DECIMAL or + * NumberFormat::PATTERN_RULEBASED. It must conform to the syntax + * described in the ICU DecimalFormat or ICU RuleBasedNumberFormat documentation + * + * @return NumberFormatter + * + * @see http://www.php.net/manual/en/numberformatter.create.php + * @see http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details + * @see http://www.icu-project.org/apiref/icu4c/classRuleBasedNumberFormat.html#_details + * + * @throws MethodArgumentValueNotImplementedException When $locale different than "en" is passed + * @throws MethodArgumentValueNotImplementedException When the $style is not supported + * @throws MethodArgumentNotImplementedException When the pattern value is different than null + */ + public static function create($locale = 'en', $style = null, $pattern = null) + { + return new self($locale, $style, $pattern); + } + + /** + * Format a currency value + * + * @param float $value The numeric currency value + * @param string $currency The 3-letter ISO 4217 currency code indicating the currency to use + * + * @return string The formatted currency value + * + * @see http://www.php.net/manual/en/numberformatter.formatcurrency.php + * @see http://www.iso.org/iso/support/faqs/faqs_widely_used_standards/widely_used_standards_other/currency_codes/currency_codes_list-1.htm + */ + public function formatCurrency($value, $currency) + { + if ($this->style == self::DECIMAL) { + return $this->format($value); + } + + $symbol = Intl::getCurrencyBundle()->getCurrencySymbol($currency, 'en'); + $fractionDigits = Intl::getCurrencyBundle()->getFractionDigits($currency); + + $value = $this->roundCurrency($value, $currency); + + $negative = false; + if (0 > $value) { + $negative = true; + $value *= -1; + } + + $value = $this->formatNumber($value, $fractionDigits); + + $ret = $symbol.$value; + + return $negative ? '('.$ret.')' : $ret; + } + + /** + * Format a number + * + * @param number $value The value to format + * @param int $type Type of the formatting, one of the format type constants. + * Only type NumberFormatter::TYPE_DEFAULT is currently supported. + * + * @return Boolean|string The formatted value or false on error + * + * @see http://www.php.net/manual/en/numberformatter.format.php + * + * @throws NotImplementedException If the method is called with the class $style 'CURRENCY' + * @throws MethodArgumentValueNotImplementedException If the $type is different than TYPE_DEFAULT + */ + public function format($value, $type = self::TYPE_DEFAULT) + { + // The original NumberFormatter does not support this format type + if ($type == self::TYPE_CURRENCY) { + trigger_error(__METHOD__.'(): Unsupported format type '.$type, \E_USER_WARNING); + + return false; + } + + if ($this->style == self::CURRENCY) { + throw new NotImplementedException(sprintf( + '%s() method does not support the formatting of currencies (instance with CURRENCY style). %s', + __METHOD__, NotImplementedException::INTL_INSTALL_MESSAGE + )); + } + + // Only the default type is supported. + if ($type != self::TYPE_DEFAULT) { + throw new MethodArgumentValueNotImplementedException(__METHOD__, 'type', $type, 'Only TYPE_DEFAULT is supported'); + } + + $fractionDigits = $this->getAttribute(self::FRACTION_DIGITS); + + $value = $this->round($value, $fractionDigits); + $value = $this->formatNumber($value, $fractionDigits); + + // behave like the intl extension + $this->resetError(); + + return $value; + } + + /** + * Returns an attribute value + * + * @param int $attr An attribute specifier, one of the numeric attribute constants + * + * @return Boolean|int The attribute value on success or false on error + * + * @see http://www.php.net/manual/en/numberformatter.getattribute.php + */ + public function getAttribute($attr) + { + return isset($this->attributes[$attr]) ? $this->attributes[$attr] : null; + } + + /** + * Returns formatter's last error code. Always returns the U_ZERO_ERROR class constant value + * + * @return int The error code from last formatter call + * + * @see http://www.php.net/manual/en/numberformatter.geterrorcode.php + */ + public function getErrorCode() + { + return $this->errorCode; + } + + /** + * Returns formatter's last error message. Always returns the U_ZERO_ERROR_MESSAGE class constant value + * + * @return string The error message from last formatter call + * + * @see http://www.php.net/manual/en/numberformatter.geterrormessage.php + */ + public function getErrorMessage() + { + return $this->errorMessage; + } + + /** + * Returns the formatter's locale + * + * The parameter $type is currently ignored. + * + * @param int $type Not supported. The locale name type to return (Locale::VALID_LOCALE or Locale::ACTUAL_LOCALE) + * + * @return string The locale used to create the formatter. Currently always + * returns "en". + * + * @see http://www.php.net/manual/en/numberformatter.getlocale.php + */ + public function getLocale($type = Locale::ACTUAL_LOCALE) + { + return 'en'; + } + + /** + * Not supported. Returns the formatter's pattern + * + * @return Boolean|string The pattern string used by the formatter or false on error + * + * @see http://www.php.net/manual/en/numberformatter.getpattern.php + * + * @throws MethodNotImplementedException + */ + public function getPattern() + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns a formatter symbol value + * + * @param int $attr A symbol specifier, one of the format symbol constants + * + * @return Boolean|string The symbol value or false on error + * + * @see http://www.php.net/manual/en/numberformatter.getsymbol.php + * + * @throws MethodNotImplementedException + */ + public function getSymbol($attr) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns a formatter text attribute value + * + * @param int $attr An attribute specifier, one of the text attribute constants + * + * @return Boolean|string The attribute value or false on error + * + * @see http://www.php.net/manual/en/numberformatter.gettextattribute.php + * + * @throws MethodNotImplementedException + */ + public function getTextAttribute($attr) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Parse a currency number + * + * @param string $value The value to parse + * @param string $currency Parameter to receive the currency name (reference) + * @param int $position Offset to begin the parsing on return this value will hold the offset at which the parsing ended + * + * @return Boolean|string The parsed numeric value of false on error + * + * @see http://www.php.net/manual/en/numberformatter.parsecurrency.php + * + * @throws MethodNotImplementedException + */ + public function parseCurrency($value, &$currency, &$position = null) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Parse a number + * + * @param string $value The value to parse + * @param int $type Type of the formatting, one of the format type constants. + * The only currently supported types are NumberFormatter::TYPE_DOUBLE, + * NumberFormatter::TYPE_INT32 and NumberFormatter::TYPE_INT64. + * @param int $position Not supported. Offset to begin the parsing on return this value will hold the offset at which the parsing ended + * + * @return Boolean|string The parsed value of false on error + * + * @see http://www.php.net/manual/en/numberformatter.parse.php + * + * @throws MethodArgumentNotImplementedException When $position different than null, behavior not implemented + */ + public function parse($value, $type = self::TYPE_DOUBLE, &$position = null) + { + if ($type == self::TYPE_DEFAULT || $type == self::TYPE_CURRENCY) { + trigger_error(__METHOD__.'(): Unsupported format type '.$type, \E_USER_WARNING); + + return false; + } + + // We don't calculate the position when parsing the value + if (null !== $position) { + throw new MethodArgumentNotImplementedException(__METHOD__, 'position'); + } + + preg_match('/^([^0-9\-]{0,})(.*)/', $value, $matches); + + // Any string before the numeric value causes error in the parsing + if (isset($matches[1]) && !empty($matches[1])) { + IntlGlobals::setError(IntlGlobals::U_PARSE_ERROR, 'Number parsing failed'); + $this->errorCode = IntlGlobals::getErrorCode(); + $this->errorMessage = IntlGlobals::getErrorMessage(); + + return false; + } + + // Remove everything that is not number or dot (.) + $value = preg_replace('/[^0-9\.\-]/', '', $value); + $value = $this->convertValueDataType($value, $type); + + // behave like the intl extension + $this->resetError(); + + return $value; + } + + /** + * Set an attribute + * + * @param int $attr An attribute specifier, one of the numeric attribute constants. + * The only currently supported attributes are NumberFormatter::FRACTION_DIGITS, + * NumberFormatter::GROUPING_USED and NumberFormatter::ROUNDING_MODE. + * @param int $value The attribute value. The only currently supported rounding modes are + * NumberFormatter::ROUND_HALFEVEN, NumberFormatter::ROUND_HALFDOWN and + * NumberFormatter::ROUND_HALFUP. + * + * @return Boolean true on success or false on failure + * + * @see http://www.php.net/manual/en/numberformatter.setattribute.php + * + * @throws MethodArgumentValueNotImplementedException When the $attr is not supported + * @throws MethodArgumentValueNotImplementedException When the $value is not supported + */ + public function setAttribute($attr, $value) + { + if (!in_array($attr, self::$supportedAttributes)) { + $message = sprintf( + 'The available attributes are: %s', + implode(', ', array_keys(self::$supportedAttributes)) + ); + + throw new MethodArgumentValueNotImplementedException(__METHOD__, 'attr', $value, $message); + } + + if (self::$supportedAttributes['ROUNDING_MODE'] == $attr && $this->isInvalidRoundingMode($value)) { + $message = sprintf( + 'The supported values for ROUNDING_MODE are: %s', + implode(', ', array_keys(self::$roundingModes)) + ); + + throw new MethodArgumentValueNotImplementedException(__METHOD__, 'attr', $value, $message); + } + + if (self::$supportedAttributes['GROUPING_USED'] == $attr) { + $value = $this->normalizeGroupingUsedValue($value); + } + + if (self::$supportedAttributes['FRACTION_DIGITS'] == $attr) { + $value = $this->normalizeFractionDigitsValue($value); + } + + $this->attributes[$attr] = $value; + $this->initializedAttributes[$attr] = true; + + return true; + } + + /** + * Not supported. Set the formatter's pattern + * + * @param string $pattern A pattern string in conformance with the ICU DecimalFormat documentation + * + * @return Boolean true on success or false on failure + * + * @see http://www.php.net/manual/en/numberformatter.setpattern.php + * @see http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details + * + * @throws MethodNotImplementedException + */ + public function setPattern($pattern) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Set the formatter's symbol + * + * @param int $attr A symbol specifier, one of the format symbol constants + * @param string $value The value for the symbol + * + * @return Boolean true on success or false on failure + * + * @see http://www.php.net/manual/en/numberformatter.setsymbol.php + * + * @throws MethodNotImplementedException + */ + public function setSymbol($attr, $value) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Set a text attribute + * + * @param int $attr An attribute specifier, one of the text attribute constants + * @param int $value The attribute value + * + * @return Boolean true on success or false on failure + * + * @see http://www.php.net/manual/en/numberformatter.settextattribute.php + * + * @throws MethodNotImplementedException + */ + public function setTextAttribute($attr, $value) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Set the error to the default U_ZERO_ERROR + */ + protected function resetError() + { + IntlGlobals::setError(IntlGlobals::U_ZERO_ERROR); + $this->errorCode = IntlGlobals::getErrorCode(); + $this->errorMessage = IntlGlobals::getErrorMessage(); + } + + /** + * Rounds a currency value, applying increment rounding if applicable + * + * When a currency have a rounding increment, an extra round is made after the first one. The rounding factor is + * determined in the ICU data and is explained as of: + * + * "the rounding increment is given in units of 10^(-fraction_digits)" + * + * The only actual rounding data as of this writing, is CHF. + * + * @param float $value The numeric currency value + * @param string $currency The 3-letter ISO 4217 currency code indicating the currency to use + * + * @return string The rounded numeric currency value + * + * @see http://en.wikipedia.org/wiki/Swedish_rounding + * @see http://www.docjar.com/html/api/com/ibm/icu/util/Currency.java.html#1007 + */ + private function roundCurrency($value, $currency) + { + $fractionDigits = Intl::getCurrencyBundle()->getFractionDigits($currency); + $roundingIncrement = Intl::getCurrencyBundle()->getRoundingIncrement($currency); + + // Round with the formatter rounding mode + $value = $this->round($value, $fractionDigits); + + // Swiss rounding + if (0 < $roundingIncrement && 0 < $fractionDigits) { + $roundingFactor = $roundingIncrement / pow(10, $fractionDigits); + $value = round($value / $roundingFactor) * $roundingFactor; + } + + return $value; + } + + /** + * Rounds a value. + * + * @param integer|float $value The value to round + * @param int $precision The number of decimal digits to round to + * + * @return integer|float The rounded value + */ + private function round($value, $precision) + { + $precision = $this->getUnitializedPrecision($value, $precision); + + $roundingMode = self::$phpRoundingMap[$this->getAttribute(self::ROUNDING_MODE)]; + $value = round($value, $precision, $roundingMode); + + return $value; + } + + /** + * Formats a number. + * + * @param integer|float $value The numeric value to format + * @param int $precision The number of decimal digits to use + * + * @return string The formatted number + */ + private function formatNumber($value, $precision) + { + $precision = $this->getUnitializedPrecision($value, $precision); + + return number_format($value, $precision, '.', $this->getAttribute(self::GROUPING_USED) ? ',' : ''); + } + + /** + * Returns the precision value if the DECIMAL style is being used and the FRACTION_DIGITS attribute is unitialized. + * + * @param integer|float $value The value to get the precision from if the FRACTION_DIGITS attribute is unitialized + * @param int $precision The precision value to returns if the FRACTION_DIGITS attribute is initialized + * + * @return int The precision value + */ + private function getUnitializedPrecision($value, $precision) + { + if ($this->style == self::CURRENCY) { + return $precision; + } + + if (!$this->isInitializedAttribute(self::FRACTION_DIGITS)) { + preg_match('/.*\.(.*)/', (string) $value, $digits); + if (isset($digits[1])) { + $precision = strlen($digits[1]); + } + } + + return $precision; + } + + /** + * Check if the attribute is initialized (value set by client code). + * + * @param string $attr The attribute name + * + * @return Boolean true if the value was set by client, false otherwise + */ + private function isInitializedAttribute($attr) + { + return isset($this->initializedAttributes[$attr]); + } + + /** + * Returns the numeric value using the $type to convert to the right data type. + * + * @param mixed $value The value to be converted + * @param int $type The type to convert. Can be TYPE_DOUBLE (float) or TYPE_INT32 (int) + * + * @return integer|float The converted value + */ + private function convertValueDataType($value, $type) + { + if ($type == self::TYPE_DOUBLE) { + $value = (float) $value; + } elseif ($type == self::TYPE_INT32) { + $value = $this->getInt32Value($value); + } elseif ($type == self::TYPE_INT64) { + $value = $this->getInt64Value($value); + } + + return $value; + } + + /** + * Convert the value data type to int or returns false if the value is out of the integer value range. + * + * @param mixed $value The value to be converted + * + * @return int The converted value + */ + private function getInt32Value($value) + { + if ($value > self::$int32Range['positive'] || $value < self::$int32Range['negative']) { + return false; + } + + return (int) $value; + } + + /** + * Convert the value data type to int or returns false if the value is out of the integer value range. + * + * @param mixed $value The value to be converted + * + * @return int|float The converted value + * + * @see https://bugs.php.net/bug.php?id=59597 Bug #59597 + */ + private function getInt64Value($value) + { + if ($value > self::$int64Range['positive'] || $value < self::$int64Range['negative']) { + return false; + } + + if (PHP_INT_SIZE !== 8 && ($value > self::$int32Range['positive'] || $value <= self::$int32Range['negative'])) { + // Bug #59597 was fixed on PHP 5.3.14 and 5.4.4 + // The negative PHP_INT_MAX was being converted to float + if ( + $value == self::$int32Range['negative'] && + ( + (version_compare(PHP_VERSION, '5.4.0', '<') && version_compare(PHP_VERSION, '5.3.14', '>=')) || + version_compare(PHP_VERSION, '5.4.4', '>=') + ) + ) { + return (int) $value; + } + + return (float) $value; + } + + if (PHP_INT_SIZE === 8) { + // Bug #59597 was fixed on PHP 5.3.14 and 5.4.4 + // A 32 bit integer was being generated instead of a 64 bit integer + if ( + ($value > self::$int32Range['positive'] || $value < self::$int32Range['negative']) && + ( + (version_compare(PHP_VERSION, '5.3.14', '<')) || + (version_compare(PHP_VERSION, '5.4.0', '>=') && version_compare(PHP_VERSION, '5.4.4', '<')) + ) + ) { + $value = (-2147483648 - ($value % -2147483648)) * ($value / abs($value)); + } + } + + return (int) $value; + } + + /** + * Check if the rounding mode is invalid. + * + * @param int $value The rounding mode value to check + * + * @return Boolean true if the rounding mode is invalid, false otherwise + */ + private function isInvalidRoundingMode($value) + { + if (in_array($value, self::$roundingModes, true)) { + return false; + } + + return true; + } + + /** + * Returns the normalized value for the GROUPING_USED attribute. Any value that can be converted to int will be + * cast to Boolean and then to int again. This way, negative values are converted to 1 and string values to 0. + * + * @param mixed $value The value to be normalized + * + * @return int The normalized value for the attribute (0 or 1) + */ + private function normalizeGroupingUsedValue($value) + { + return (int) (Boolean) (int) $value; + } + + /** + * Returns the normalized value for the FRACTION_DIGITS attribute. The value is converted to int and if negative, + * the returned value will be 0. + * + * @param mixed $value The value to be normalized + * + * @return int The normalized value for the attribute + */ + private function normalizeFractionDigitsValue($value) + { + $value = (int) $value; + + return (0 > $value) ? 0 : $value; + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/README.md b/vendor/symfony/intl/Symfony/Component/Intl/README.md new file mode 100644 index 00000000..ef4ba505 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/README.md @@ -0,0 +1,25 @@ +Intl Component +============= + +A PHP replacement layer for the C intl extension that includes additional data +from the ICU library. + +The replacement layer is limited to the locale "en". If you want to use other +locales, you should [install the intl extension] [1] instead. + +Documentation +------------- + +The documentation for the component can be found [online] [2]. + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/Intl/ + $ composer.phar install --dev + $ phpunit + +[0]: http://www.php.net/manual/en/intl.setup.php +[1]: http://symfony.com/doc/2.3/components/intl.html diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/AbstractBundle.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/AbstractBundle.php new file mode 100644 index 00000000..d1d523c4 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/AbstractBundle.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle; + +use Symfony\Component\Intl\Intl; +use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface; + +/** + * Base class for {@link ResourceBundleInterface} implementations. + * + * @author Bernhard Schussek + */ +abstract class AbstractBundle implements ResourceBundleInterface +{ + /** + * @var string + */ + private $path; + + /** + * @var StructuredBundleReaderInterface + */ + private $reader; + + /** + * Creates a bundle at the given path using the given reader for reading + * bundle entries. + * + * @param string $path The path to the bundle. + * @param StructuredBundleReaderInterface $reader The reader for reading + * the bundle. + */ + public function __construct($path, StructuredBundleReaderInterface $reader) + { + $this->path = $path; + $this->reader = $reader; + } + + /** + * {@inheritdoc} + */ + public function getLocales() + { + return $this->reader->getLocales($this->path); + } + + /** + * Proxy method for {@link StructuredBundleReaderInterface#read}. + */ + protected function read($locale) + { + return $this->reader->read($this->path, $locale); + } + + /** + * Proxy method for {@link StructuredBundleReaderInterface#readEntry}. + */ + protected function readEntry($locale, array $indices, $mergeFallback = false) + { + return $this->reader->readEntry($this->path, $locale, $indices, $mergeFallback); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Compiler/BundleCompiler.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Compiler/BundleCompiler.php new file mode 100644 index 00000000..174aa179 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Compiler/BundleCompiler.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle\Compiler; + +use Symfony\Component\Intl\Exception\RuntimeException; + +/** + * Compiles .txt resource bundles to binary .res files. + * + * @author Bernhard Schussek + */ +class BundleCompiler implements BundleCompilerInterface +{ + /** + * @var string The path to the "genrb" executable. + */ + private $genrb; + + /** + * Creates a new compiler based on the "genrb" executable. + * + * @param string $genrb Optional. The path to the "genrb" executable. + * @param string $envVars Optional. Environment variables to be loaded when + * running "genrb". + * + * @throws RuntimeException If the "genrb" cannot be found. + */ + public function __construct($genrb = 'genrb', $envVars = '') + { + exec('which ' . $genrb, $output, $status); + + if (0 !== $status) { + throw new RuntimeException(sprintf( + 'The command "%s" is not installed', + $genrb + )); + } + + $this->genrb = ($envVars ? $envVars . ' ' : '') . $genrb; + } + + /** + * {@inheritdoc} + */ + public function compile($sourcePath, $targetDir) + { + if (is_dir($sourcePath)) { + $sourcePath .= '/*.txt'; + } + + exec($this->genrb.' --quiet -e UTF-8 -d '.$targetDir.' '.$sourcePath, $output, $status); + + if ($status !== 0) { + throw new RuntimeException(sprintf( + 'genrb failed with status %d while compiling %s to %s.', + $status, + $sourcePath, + $targetDir + )); + } + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Compiler/BundleCompilerInterface.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Compiler/BundleCompilerInterface.php new file mode 100644 index 00000000..6184ea3e --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Compiler/BundleCompilerInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle\Compiler; + +/** + * Compiles a resource bundle. + * + * @author Bernhard Schussek + */ +interface BundleCompilerInterface +{ + /** + * Compiles a resource bundle at the given source to the given target + * directory. + * + * @param string $sourcePath + * @param string $targetDir + */ + public function compile($sourcePath, $targetDir); +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/CurrencyBundle.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/CurrencyBundle.php new file mode 100644 index 00000000..6f2a0ed3 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/CurrencyBundle.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle; + +/** + * Default implementation of {@link CurrencyBundleInterface}. + * + * @author Bernhard Schussek + */ +class CurrencyBundle extends AbstractBundle implements CurrencyBundleInterface +{ + const INDEX_NAME = 0; + + const INDEX_SYMBOL = 1; + + const INDEX_FRACTION_DIGITS = 2; + + const INDEX_ROUNDING_INCREMENT = 3; + + /** + * {@inheritdoc} + */ + public function getCurrencySymbol($currency, $locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + return $this->readEntry($locale, array('Currencies', $currency, static::INDEX_SYMBOL)); + } + + /** + * {@inheritdoc} + */ + public function getCurrencyName($currency, $locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + return $this->readEntry($locale, array('Currencies', $currency, static::INDEX_NAME)); + } + + /** + * {@inheritdoc} + */ + public function getCurrencyNames($locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + if (null === ($currencies = $this->readEntry($locale, array('Currencies')))) { + return array(); + } + + if ($currencies instanceof \Traversable) { + $currencies = iterator_to_array($currencies); + } + + $index = static::INDEX_NAME; + + array_walk($currencies, function (&$value) use ($index) { + $value = $value[$index]; + }); + + return $currencies; + } + + /** + * {@inheritdoc} + */ + public function getFractionDigits($currency) + { + return $this->readEntry('en', array('Currencies', $currency, static::INDEX_FRACTION_DIGITS)); + } + + /** + * {@inheritdoc} + */ + public function getRoundingIncrement($currency) + { + return $this->readEntry('en', array('Currencies', $currency, static::INDEX_ROUNDING_INCREMENT)); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/CurrencyBundleInterface.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/CurrencyBundleInterface.php new file mode 100644 index 00000000..1a88e937 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/CurrencyBundleInterface.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle; + +/** + * Gives access to currency-related ICU data. + * + * @author Bernhard Schussek + */ +interface CurrencyBundleInterface extends ResourceBundleInterface +{ + /** + * Returns the symbol used for a currency. + * + * @param string $currency A currency code (e.g. "EUR"). + * @param string $locale Optional. The locale to return the result in. + * Defaults to {@link \Locale::getDefault()}. + * + * @return string|null The currency symbol or NULL if not found. + */ + public function getCurrencySymbol($currency, $locale = null); + + /** + * Returns the name of a currency. + * + * @param string $currency A currency code (e.g. "EUR"). + * @param string $locale Optional. The locale to return the name in. + * Defaults to {@link \Locale::getDefault()}. + * + * @return string|null The name of the currency or NULL if not found. + */ + public function getCurrencyName($currency, $locale = null); + + /** + * Returns the names of all known currencies. + * + * @param string $locale Optional. The locale to return the names in. + * Defaults to {@link \Locale::getDefault()}. + * + * @return string[] A list of currency names indexed by currency codes. + */ + public function getCurrencyNames($locale = null); + + /** + * Returns the number of digits after the comma of a currency. + * + * @param string $currency A currency code (e.g. "EUR"). + * + * @return integer|null The number of digits after the comma or NULL if not found. + */ + public function getFractionDigits($currency); + + /** + * Returns the rounding increment of a currency. + * + * The rounding increment indicates to which number a currency is rounded. + * For example, 1230 rounded to the nearest 50 is 1250. 1.234 rounded to the + * nearest 0.65 is 1.3. + * + * @param string $currency A currency code (e.g. "EUR"). + * + * @return float|integer|null The rounding increment or NULL if not found. + */ + public function getRoundingIncrement($currency); +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/LanguageBundle.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/LanguageBundle.php new file mode 100644 index 00000000..c449f9c1 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/LanguageBundle.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle; + +/** + * Default implementation of {@link LanguageBundleInterface}. + * + * @author Bernhard Schussek + */ +class LanguageBundle extends AbstractBundle implements LanguageBundleInterface +{ + /** + * {@inheritdoc} + */ + public function getLanguageName($lang, $region = null, $locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + if (null === ($languages = $this->readEntry($locale, array('Languages')))) { + return null; + } + + // Some languages are translated together with their region, + // i.e. "en_GB" is translated as "British English" + if (null !== $region && isset($languages[$lang.'_'.$region])) { + return $languages[$lang.'_'.$region]; + } + + return $languages[$lang]; + } + + /** + * {@inheritdoc} + */ + public function getLanguageNames($locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + if (null === ($languages = $this->readEntry($locale, array('Languages')))) { + return array(); + } + + if ($languages instanceof \Traversable) { + $languages = iterator_to_array($languages); + } + + return $languages; + } + + /** + * {@inheritdoc} + */ + public function getScriptName($script, $lang = null, $locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + $data = $this->read($locale); + + // Some languages are translated together with their script, + // e.g. "zh_Hans" is translated as "Simplified Chinese" + if (null !== $lang && isset($data['Languages'][$lang.'_'.$script])) { + $langName = $data['Languages'][$lang.'_'.$script]; + + // If the script is appended in braces, extract it, e.g. "zh_Hans" + // is translated as "Chinesisch (vereinfacht)" in locale "de" + if (strpos($langName, '(') !== false) { + list($langName, $scriptName) = preg_split('/[\s()]/', $langName, null, PREG_SPLIT_NO_EMPTY); + + return $scriptName; + } + } + + // "af" (Afrikaans) has no "Scripts" block + if (!isset($data['Scripts'][$script])) { + return null; + } + + return $data['Scripts'][$script]; + } + + /** + * {@inheritdoc} + */ + public function getScriptNames($locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + if (null === ($scripts = $this->readEntry($locale, array('Scripts')))) { + return array(); + } + + if ($scripts instanceof \Traversable) { + $scripts = iterator_to_array($scripts); + } + + return $scripts; + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/LanguageBundleInterface.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/LanguageBundleInterface.php new file mode 100644 index 00000000..de50bda0 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/LanguageBundleInterface.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle; + +/** + * Gives access to language-related ICU data. + * + * @author Bernhard Schussek + */ +interface LanguageBundleInterface extends ResourceBundleInterface +{ + /** + * Returns the name of a language. + * + * @param string $lang A language code (e.g. "en"). + * @param string|null $region Optional. A region code (e.g. "US"). + * @param string $locale Optional. The locale to return the name in. + * Defaults to {@link \Locale::getDefault()}. + * + * @return string|null The name of the language or NULL if not found. + */ + public function getLanguageName($lang, $region = null, $locale = null); + + /** + * Returns the names of all known languages. + * + * @param string $locale Optional. The locale to return the names in. + * Defaults to {@link \Locale::getDefault()}. + * + * @return string[] A list of language names indexed by language codes. + */ + public function getLanguageNames($locale = null); + + /** + * Returns the name of a script. + * + * @param string $script A script code (e.g. "Hans"). + * @param string $lang Optional. A language code (e.g. "zh"). + * @param string $locale Optional. The locale to return the name in. + * Defaults to {@link \Locale::getDefault()}. + * + * @return string|null The name of the script or NULL if not found. + */ + public function getScriptName($script, $lang = null, $locale = null); + + /** + * Returns the names of all known scripts. + * + * @param string $locale Optional. The locale to return the names in. + * Defaults to {@link \Locale::getDefault()}. + * + * @return string[] A list of script names indexed by script codes. + */ + public function getScriptNames($locale = null); +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/LocaleBundle.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/LocaleBundle.php new file mode 100644 index 00000000..6f6cdfcb --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/LocaleBundle.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle; + +/** + * Default implementation of {@link LocaleBundleInterface}. + * + * @author Bernhard Schussek + */ +class LocaleBundle extends AbstractBundle implements LocaleBundleInterface +{ + /** + * {@inheritdoc} + */ + public function getLocaleName($ofLocale, $locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + return $this->readEntry($locale, array('Locales', $ofLocale)); + } + + /** + * {@inheritdoc} + */ + public function getLocaleNames($locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + if (null === ($locales = $this->readEntry($locale, array('Locales')))) { + return array(); + } + + if ($locales instanceof \Traversable) { + $locales = iterator_to_array($locales); + } + + return $locales; + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/LocaleBundleInterface.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/LocaleBundleInterface.php new file mode 100644 index 00000000..daf5be68 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/LocaleBundleInterface.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle; + +/** + * Gives access to locale-related ICU data. + * + * @author Bernhard Schussek + */ +interface LocaleBundleInterface extends ResourceBundleInterface +{ + /** + * Returns the name of a locale. + * + * @param string $ofLocale The locale to return the name of (e.g. "de_AT"). + * @param string $locale Optional. The locale to return the name in. + * Defaults to {@link \Locale::getDefault()}. + * + * @return string|null The name of the locale or NULL if not found. + */ + public function getLocaleName($ofLocale, $locale = null); + + /** + * Returns the names of all known locales. + * + * @param string $locale Optional. The locale to return the names in. + * Defaults to {@link \Locale::getDefault()}. + * + * @return string[] A list of locale names indexed by locale codes. + */ + public function getLocaleNames($locale = null); +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/AbstractBundleReader.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/AbstractBundleReader.php new file mode 100644 index 00000000..c30693ac --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/AbstractBundleReader.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle\Reader; + +/** + * Base class for {@link BundleReaderInterface} implementations. + * + * @author Bernhard Schussek + */ +abstract class AbstractBundleReader implements BundleReaderInterface +{ + /** + * {@inheritdoc} + */ + public function getLocales($path) + { + $extension = '.' . $this->getFileExtension(); + $locales = glob($path . '/*' . $extension); + + // Remove file extension and sort + array_walk($locales, function (&$locale) use ($extension) { $locale = basename($locale, $extension); }); + sort($locales); + + return $locales; + } + + /** + * Returns the extension of locale files in this bundle. + * + * @return string The file extension (without leading dot). + */ + abstract protected function getFileExtension(); +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/BinaryBundleReader.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/BinaryBundleReader.php new file mode 100644 index 00000000..56cef806 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/BinaryBundleReader.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle\Reader; + +use Symfony\Component\Intl\Exception\RuntimeException; +use Symfony\Component\Intl\ResourceBundle\Util\ArrayAccessibleResourceBundle; + +/** + * Reads binary .res resource bundles. + * + * @author Bernhard Schussek + */ +class BinaryBundleReader extends AbstractBundleReader implements BundleReaderInterface +{ + /** + * {@inheritdoc} + */ + public function read($path, $locale) + { + // Point for future extension: Modify this class so that it works also + // if the \ResourceBundle class is not available. + $bundle = new \ResourceBundle($locale, $path); + + if (null === $bundle) { + throw new RuntimeException(sprintf( + 'Could not load the resource bundle "%s/%s.res".', + $path, + $locale + )); + } + + return new ArrayAccessibleResourceBundle($bundle); + } + + /** + * {@inheritdoc} + */ + protected function getFileExtension() + { + return 'res'; + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/BufferedBundleReader.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/BufferedBundleReader.php new file mode 100644 index 00000000..e44074b1 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/BufferedBundleReader.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle\Reader; + +use Symfony\Component\Intl\ResourceBundle\Util\RingBuffer; + +/** + * @author Bernhard Schussek + */ +class BufferedBundleReader implements BundleReaderInterface +{ + /** + * @var BundleReaderInterface + */ + private $reader; + + private $buffer; + + /** + * Buffers a given reader. + * + * @param BundleReaderInterface $reader The reader to buffer. + * @param integer $bufferSize The number of entries to store + * in the buffer. + */ + public function __construct(BundleReaderInterface $reader, $bufferSize) + { + $this->reader = $reader; + $this->buffer = new RingBuffer($bufferSize); + } + + /** + * {@inheritdoc} + */ + public function read($path, $locale) + { + $hash = $path . '//' . $locale; + + if (!isset($this->buffer[$hash])) { + $this->buffer[$hash] = $this->reader->read($path, $locale); + } + + return $this->buffer[$hash]; + } + + /** + * {@inheritdoc} + */ + public function getLocales($path) + { + return $this->reader->getLocales($path); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/BundleReaderInterface.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/BundleReaderInterface.php new file mode 100644 index 00000000..bc485cd5 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/BundleReaderInterface.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle\Reader; + +/** + * Reads resource bundle files. + * + * @author Bernhard Schussek + */ +interface BundleReaderInterface +{ + /** + * Reads a resource bundle. + * + * @param string $path The path to the resource bundle. + * @param string $locale The locale to read. + * + * @return mixed Returns an array or {@link \ArrayAccess} instance for + * complex data, a scalar value otherwise. + */ + public function read($path, $locale); + + /** + * Reads the available locales of a resource bundle. + * + * @param string $path The path to the resource bundle. + * + * @return string[] A list of supported locale codes. + */ + public function getLocales($path); +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/PhpBundleReader.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/PhpBundleReader.php new file mode 100644 index 00000000..663bcc9d --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/PhpBundleReader.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle\Reader; + +use Symfony\Component\Intl\Exception\InvalidArgumentException; +use Symfony\Component\Intl\Exception\RuntimeException; + +/** + * Reads .php resource bundles. + * + * @author Bernhard Schussek + */ +class PhpBundleReader extends AbstractBundleReader implements BundleReaderInterface +{ + /** + * {@inheritdoc} + */ + public function read($path, $locale) + { + if ('en' !== $locale) { + throw new InvalidArgumentException('Only the locale "en" is supported.'); + } + + $fileName = $path . '/' . $locale . '.php'; + + if (!file_exists($fileName)) { + throw new RuntimeException(sprintf( + 'The resource bundle "%s/%s.php" does not exist.', + $path, + $locale + )); + } + + if (!is_file($fileName)) { + throw new RuntimeException(sprintf( + 'The resource bundle "%s/%s.php" is not a file.', + $path, + $locale + )); + } + + return include $fileName; + } + + /** + * {@inheritdoc} + */ + protected function getFileExtension() + { + return 'php'; + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReader.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReader.php new file mode 100644 index 00000000..e3656fe2 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReader.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle\Reader; + +use Symfony\Component\Intl\ResourceBundle\Util\RecursiveArrayAccess; + +/** + * A structured reader wrapping an existing resource bundle reader. + * + * @author Bernhard Schussek + * + * @see StructuredResourceBundleBundleReaderInterface + */ +class StructuredBundleReader implements StructuredBundleReaderInterface +{ + /** + * @var BundleReaderInterface + */ + private $reader; + + /** + * Creates an entry reader based on the given resource bundle reader. + * + * @param BundleReaderInterface $reader A resource bundle reader to use. + */ + public function __construct(BundleReaderInterface $reader) + { + $this->reader = $reader; + } + + /** + * {@inheritdoc} + */ + public function read($path, $locale) + { + return $this->reader->read($path, $locale); + } + + /** + * {@inheritdoc} + */ + public function getLocales($path) + { + return $this->reader->getLocales($path); + } + + /** + * {@inheritdoc} + */ + public function readEntry($path, $locale, array $indices, $fallback = true) + { + $data = $this->reader->read($path, $locale); + + $entry = RecursiveArrayAccess::get($data, $indices); + $multivalued = is_array($entry) || $entry instanceof \Traversable; + + if (!($fallback && (null === $entry || $multivalued))) { + return $entry; + } + + if (null !== ($fallbackLocale = $this->getFallbackLocale($locale))) { + $parentEntry = $this->readEntry($path, $fallbackLocale, $indices, true); + + if ($entry || $parentEntry) { + $multivalued = $multivalued || is_array($parentEntry) || $parentEntry instanceof \Traversable; + + if ($multivalued) { + if ($entry instanceof \Traversable) { + $entry = iterator_to_array($entry); + } + + if ($parentEntry instanceof \Traversable) { + $parentEntry = iterator_to_array($parentEntry); + } + + $entry = array_merge( + $parentEntry ?: array(), + $entry ?: array() + ); + } else { + $entry = null === $entry ? $parentEntry : $entry; + } + } + } + + return $entry; + } + + /** + * Returns the fallback locale for a given locale, if any + * + * @param string $locale The locale to find the fallback for. + * + * @return string|null The fallback locale, or null if no parent exists + */ + private function getFallbackLocale($locale) + { + if (false === $pos = strrpos($locale, '_')) { + return null; + } + + return substr($locale, 0, $pos); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReaderInterface.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReaderInterface.php new file mode 100644 index 00000000..c22ad93b --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReaderInterface.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle\Reader; + +/** + * Reads individual entries of a resource file. + * + * @author Bernhard Schussek + */ +interface StructuredBundleReaderInterface extends BundleReaderInterface +{ + /** + * Reads an entry from a resource bundle. + * + * An entry can be selected from the resource bundle by passing the path + * to that entry in the bundle. For example, if the bundle is structured + * like this: + * + * TopLevel + * NestedLevel + * Entry: Value + * + * Then the value can be read by calling: + * + * $reader->readEntry('...', 'en', array('TopLevel', 'NestedLevel', 'Entry')); + * + * @param string $path The path to the resource bundle. + * @param string $locale The locale to read. + * @param string[] $indices The indices to read from the bundle. + * @param Boolean $fallback Whether to merge the value with the value from + * the fallback locale (e.g. "en" for "en_GB"). + * Only applicable if the result is multivalued + * (i.e. array or \ArrayAccess) or cannot be found + * in the requested locale. + * + * @return mixed Returns an array or {@link \ArrayAccess} instance for + * complex data, a scalar value for simple data and NULL + * if the given path could not be accessed. + */ + public function readEntry($path, $locale, array $indices, $fallback = true); +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/RegionBundle.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/RegionBundle.php new file mode 100644 index 00000000..a3cd9bd3 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/RegionBundle.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle; + +/** + * Default implementation of {@link RegionBundleInterface}. + * + * @author Bernhard Schussek + */ +class RegionBundle extends AbstractBundle implements RegionBundleInterface +{ + /** + * {@inheritdoc} + */ + public function getCountryName($country, $locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + return $this->readEntry($locale, array('Countries', $country)); + } + + /** + * {@inheritdoc} + */ + public function getCountryNames($locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + if (null === ($countries = $this->readEntry($locale, array('Countries')))) { + return array(); + } + + if ($countries instanceof \Traversable) { + $countries = iterator_to_array($countries); + } + + return $countries; + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/RegionBundleInterface.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/RegionBundleInterface.php new file mode 100644 index 00000000..4e55f2dc --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/RegionBundleInterface.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle; + +/** + * Gives access to region-related ICU data. + * + * @author Bernhard Schussek + */ +interface RegionBundleInterface extends ResourceBundleInterface +{ + /** + * Returns the name of a country. + * + * @param string $country A country code (e.g. "US"). + * @param string $locale Optional. The locale to return the name in. + * Defaults to {@link \Locale::getDefault()}. + * + * @return string|null The name of the country or NULL if not found. + */ + public function getCountryName($country, $locale = null); + + /** + * Returns the names of all known countries. + * + * @param string $locale Optional. The locale to return the names in. + * Defaults to {@link \Locale::getDefault()}. + * + * @return string[] A list of country names indexed by country codes. + */ + public function getCountryNames($locale = null); +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/ResourceBundleInterface.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/ResourceBundleInterface.php new file mode 100644 index 00000000..497a66a3 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/ResourceBundleInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle; + +/** + * Gives access to ICU data. + * + * @author Bernhard Schussek + */ +interface ResourceBundleInterface +{ + /** + * Returns the list of locales that this bundle supports. + * + * @return string[] A list of locale codes. + */ + public function getLocales(); +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/BundleTransformer.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/BundleTransformer.php new file mode 100644 index 00000000..0692d6fe --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/BundleTransformer.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle\Transformer; + +use Symfony\Component\Intl\Exception\RuntimeException; +use Symfony\Component\Intl\ResourceBundle\Transformer\Rule\TransformationRuleInterface; +use Symfony\Component\Intl\ResourceBundle\Writer\PhpBundleWriter; + +/** + * Compiles a number of resource bundles based on predefined compilation rules. + * + * @author Bernhard Schussek + */ +class BundleTransformer +{ + /** + * @var TransformationRuleInterface[] + */ + private $rules = array(); + + /** + * Adds a new compilation rule. + * + * @param TransformationRuleInterface $rule The compilation rule. + */ + public function addRule(TransformationRuleInterface $rule) + { + $this->rules[] = $rule; + } + + /** + * Runs the compilation with the given compilation context. + * + * @param CompilationContextInterface $context The context storing information + * needed to run the compilation. + * + * @throws RuntimeException If any of the files to be compiled by the loaded + * compilation rules does not exist. + */ + public function compileBundles(CompilationContextInterface $context) + { + $filesystem = $context->getFilesystem(); + $compiler = $context->getCompiler(); + + $filesystem->remove($context->getBinaryDir()); + $filesystem->mkdir($context->getBinaryDir()); + + foreach ($this->rules as $rule) { + $filesystem->mkdir($context->getBinaryDir() . '/' . $rule->getBundleName()); + + $resources = (array) $rule->beforeCompile($context); + + foreach ($resources as $resource) { + if (!file_exists($resource)) { + throw new RuntimeException(sprintf( + 'The file "%s" to be compiled by %s does not exist.', + $resource, + get_class($rule) + )); + } + + $compiler->compile($resource, $context->getBinaryDir() . '/' . $rule->getBundleName()); + } + + $rule->afterCompile($context); + } + } + + public function createStubs(StubbingContextInterface $context) + { + $filesystem = $context->getFilesystem(); + $phpWriter = new PhpBundleWriter(); + + $filesystem->remove($context->getStubDir()); + $filesystem->mkdir($context->getStubDir()); + + foreach ($this->rules as $rule) { + $filesystem->mkdir($context->getStubDir() . '/' . $rule->getBundleName()); + + $data = $rule->beforeCreateStub($context); + + $phpWriter->write($context->getStubDir() . '/' . $rule->getBundleName(), 'en', $data); + + $rule->afterCreateStub($context); + } + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContext.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContext.php new file mode 100644 index 00000000..cdc1951b --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContext.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle\Transformer; + +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Intl\ResourceBundle\Compiler\BundleCompilerInterface; + +/** + * Default implementation of {@link CompilationContextInterface}. + * + * @author Bernhard Schussek + */ +class CompilationContext implements CompilationContextInterface +{ + /** + * @var string + */ + private $sourceDir; + + /** + * @var string + */ + private $binaryDir; + + /** + * @var FileSystem + */ + private $filesystem; + + /** + * @var BundleCompilerInterface + */ + private $compiler; + + /** + * @var string + */ + private $icuVersion; + + public function __construct($sourceDir, $binaryDir, Filesystem $filesystem, BundleCompilerInterface $compiler, $icuVersion) + { + $this->sourceDir = $sourceDir; + $this->binaryDir = $binaryDir; + $this->filesystem = $filesystem; + $this->compiler = $compiler; + $this->icuVersion = $icuVersion; + } + + /** + * {@inheritdoc} + */ + public function getSourceDir() + { + return $this->sourceDir; + } + + /** + * {@inheritdoc} + */ + public function getBinaryDir() + { + return $this->binaryDir; + } + + /** + * {@inheritdoc} + */ + public function getFilesystem() + { + return $this->filesystem; + } + + /** + * {@inheritdoc} + */ + public function getCompiler() + { + return $this->compiler; + } + + /** + * {@inheritdoc} + */ + public function getIcuVersion() + { + return $this->icuVersion; + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContextInterface.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContextInterface.php new file mode 100644 index 00000000..f05c2807 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContextInterface.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle\Transformer; + +/** + * Stores contextual information for resource bundle compilation. + * + * @author Bernhard Schussek + */ +interface CompilationContextInterface +{ + /** + * Returns the directory where the source versions of the resource bundles + * are stored. + * + * @return string An absolute path to a directory. + */ + public function getSourceDir(); + + /** + * Returns the directory where the binary resource bundles are stored. + * + * @return string An absolute path to a directory. + */ + public function getBinaryDir(); + + /** + * Returns a tool for manipulating the filesystem. + * + * @return \Symfony\Component\Filesystem\Filesystem The filesystem manipulator. + */ + public function getFilesystem(); + + /** + * Returns a resource bundle compiler. + * + * @return \Symfony\Component\Intl\ResourceBundle\Compiler\BundleCompilerInterface The loaded resource bundle compiler. + */ + public function getCompiler(); + + /** + * Returns the ICU version of the bundles being converted. + * + * @return string The ICU version string. + */ + public function getIcuVersion(); +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/CurrencyBundleTransformationRule.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/CurrencyBundleTransformationRule.php new file mode 100644 index 00000000..95783b3b --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/CurrencyBundleTransformationRule.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle\Transformer\Rule; + +use Symfony\Component\Intl\Intl; +use Symfony\Component\Intl\ResourceBundle\CurrencyBundle; +use Symfony\Component\Intl\ResourceBundle\Transformer\CompilationContextInterface; +use Symfony\Component\Intl\ResourceBundle\Transformer\StubbingContextInterface; +use Symfony\Component\Intl\Util\IcuVersion; + +/** + * The rule for compiling the currency bundle. + * + * @author Bernhard Schussek + */ +class CurrencyBundleTransformationRule implements TransformationRuleInterface +{ + /** + * {@inheritdoc} + */ + public function getBundleName() + { + return 'curr'; + } + + /** + * {@inheritdoc} + */ + public function beforeCompile(CompilationContextInterface $context) + { + // The currency data is contained in the locales and misc bundles + // in ICU <= 4.2 + if (IcuVersion::compare($context->getIcuVersion(), '4.2', '<=', 1)) { + return array( + $context->getSourceDir() . '/misc/supplementalData.txt', + $context->getSourceDir() . '/locales' + ); + } + + return $context->getSourceDir() . '/curr'; + } + + /** + * {@inheritdoc} + */ + public function afterCompile(CompilationContextInterface $context) + { + // \ResourceBundle does not like locale names with uppercase chars, so rename + // the resource file + // See: http://bugs.php.net/bug.php?id=54025 + $fileName = $context->getBinaryDir() . '/curr/supplementalData.res'; + $fileNameLower = $context->getBinaryDir() . '/curr/supplementaldata.res'; + + $context->getFilesystem()->rename($fileName, $fileNameLower); + } + + /** + * {@inheritdoc} + */ + public function beforeCreateStub(StubbingContextInterface $context) + { + $currencies = array(); + $currencyBundle = Intl::getCurrencyBundle(); + + foreach ($currencyBundle->getCurrencyNames('en') as $code => $name) { + $currencies[$code] = array( + CurrencyBundle::INDEX_NAME => $name, + CurrencyBundle::INDEX_SYMBOL => $currencyBundle->getCurrencySymbol($code, 'en'), + CurrencyBundle::INDEX_FRACTION_DIGITS => $currencyBundle->getFractionDigits($code), + CurrencyBundle::INDEX_ROUNDING_INCREMENT => $currencyBundle->getRoundingIncrement($code), + ); + } + + return array( + 'Currencies' => $currencies, + ); + } + + /** + * {@inheritdoc} + */ + public function afterCreateStub(StubbingContextInterface $context) + { + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LanguageBundleTransformationRule.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LanguageBundleTransformationRule.php new file mode 100644 index 00000000..5e6f9018 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LanguageBundleTransformationRule.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle\Transformer\Rule; + +use Symfony\Component\Intl\Intl; +use Symfony\Component\Intl\ResourceBundle\Transformer\CompilationContextInterface; +use Symfony\Component\Intl\ResourceBundle\Transformer\StubbingContextInterface; +use Symfony\Component\Intl\Util\IcuVersion; + +/** + * The rule for compiling the language bundle. + * + * @author Bernhard Schussek + */ +class LanguageBundleTransformationRule implements TransformationRuleInterface +{ + /** + * {@inheritdoc} + */ + public function getBundleName() + { + return 'lang'; + } + + /** + * {@inheritdoc} + */ + public function beforeCompile(CompilationContextInterface $context) + { + // The language data is contained in the locales bundle in ICU <= 4.2 + if (IcuVersion::compare($context->getIcuVersion(), '4.2', '<=', 1)) { + return $context->getSourceDir() . '/locales'; + } + + return $context->getSourceDir() . '/lang'; + } + + /** + * {@inheritdoc} + */ + public function afterCompile(CompilationContextInterface $context) + { + } + + /** + * {@inheritdoc} + */ + public function beforeCreateStub(StubbingContextInterface $context) + { + return array( + 'Languages' => Intl::getLanguageBundle()->getLanguageNames('en'), + 'Scripts' => Intl::getLanguageBundle()->getScriptNames('en'), + ); + } + + /** + * {@inheritdoc} + */ + public function afterCreateStub(StubbingContextInterface $context) + { + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LocaleBundleTransformationRule.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LocaleBundleTransformationRule.php new file mode 100644 index 00000000..b2576d6e --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LocaleBundleTransformationRule.php @@ -0,0 +1,251 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle\Transformer\Rule; + +use Symfony\Component\Intl\Exception\RuntimeException; +use Symfony\Component\Intl\Intl; +use Symfony\Component\Intl\ResourceBundle\Transformer\CompilationContextInterface; +use Symfony\Component\Intl\ResourceBundle\Transformer\StubbingContextInterface; +use Symfony\Component\Intl\ResourceBundle\Writer\TextBundleWriter; + +/** + * The rule for compiling the locale bundle. + * + * @author Bernhard Schussek + */ +class LocaleBundleTransformationRule implements TransformationRuleInterface +{ + /** + * @var \Symfony\Component\Intl\ResourceBundle\LanguageBundleInterface + */ + private $languageBundle; + + /** + * @var \Symfony\Component\Intl\ResourceBundle\RegionBundleInterface + */ + private $regionBundle; + + public function __construct() + { + $this->languageBundle = Intl::getLanguageBundle(); + $this->regionBundle = Intl::getRegionBundle(); + } + + /** + * {@inheritdoc} + */ + public function getBundleName() + { + return 'locales'; + } + + /** + * {@inheritdoc} + */ + public function beforeCompile(CompilationContextInterface $context) + { + $tempDir = sys_get_temp_dir() . '/icu-data-locales'; + + $context->getFilesystem()->remove($tempDir); + $context->getFilesystem()->mkdir($tempDir); + + $this->generateTextFiles($tempDir, $this->scanLocales($context)); + + return $tempDir; + } + + /** + * {@inheritdoc} + */ + public function afterCompile(CompilationContextInterface $context) + { + $context->getFilesystem()->remove(sys_get_temp_dir() . '/icu-data-locales'); + } + + /** + * {@inheritdoc} + */ + public function beforeCreateStub(StubbingContextInterface $context) + { + return array( + 'Locales' => Intl::getLocaleBundle()->getLocaleNames('en'), + ); + } + + /** + * {@inheritdoc} + */ + public function afterCreateStub(StubbingContextInterface $context) + { + } + + private function scanLocales(CompilationContextInterface $context) + { + $tempDir = sys_get_temp_dir() . '/icu-data-locales-source'; + + $context->getFilesystem()->remove($tempDir); + $context->getFilesystem()->mkdir($tempDir); + + // Temporarily generate the resource bundles + $context->getCompiler()->compile($context->getSourceDir() . '/locales', $tempDir); + + // Discover the list of supported locales, which are the names of the resource + // bundles in the "locales" directory + $locales = glob($tempDir . '/*.res'); + + // Remove file extension and sort + array_walk($locales, function (&$locale) { $locale = basename($locale, '.res'); }); + sort($locales); + + // Delete unneeded locales + foreach ($locales as $key => $locale) { + // Delete all aliases from the list + // i.e., "az_AZ" is an alias for "az_Latn_AZ" + $content = file_get_contents($context->getSourceDir() . '/locales/' . $locale . '.txt'); + + // The key "%%ALIAS" is not accessible through the \ResourceBundle class, + // so look in the original .txt file instead + if (strpos($content, '%%ALIAS') !== false) { + unset($locales[$key]); + } + + // Delete locales that have no content (i.e. only "Version" key) + $bundle = new \ResourceBundle($locale, $tempDir); + + if (null === $bundle) { + throw new RuntimeException('The resource bundle for locale ' . $locale . ' could not be loaded from directory ' . $tempDir); + } + + // There seems to be no other way for identifying all keys in this specific + // resource bundle + if (array_keys(iterator_to_array($bundle)) === array('Version')) { + unset($locales[$key]); + } + } + + $context->getFilesystem()->remove($tempDir); + + return $locales; + } + + private function generateTextFiles($targetDirectory, array $locales) + { + $displayLocales = array_unique(array_merge( + $this->languageBundle->getLocales(), + $this->regionBundle->getLocales() + )); + + $txtWriter = new TextBundleWriter(); + + // Generate a list of locale names in the language of each display locale + // Each locale name has the form: "Language (Script, Region, Variant1, ...) + // Script, Region and Variants are optional. If none of them is available, + // the braces are not printed. + foreach ($displayLocales as $displayLocale) { + // Don't include ICU's root resource bundle + if ('root' === $displayLocale) { + continue; + } + + $names = array(); + + foreach ($locales as $locale) { + // Don't include ICU's root resource bundle + if ($locale === 'root') { + continue; + } + + if (null !== ($name = $this->generateLocaleName($locale, $displayLocale))) { + $names[$locale] = $name; + } + } + + // If no names could be generated for the current locale, skip it + if (0 === count($names)) { + continue; + } + + $txtWriter->write($targetDirectory, $displayLocale, array('Locales' => $names)); + } + } + + private function generateLocaleName($locale, $displayLocale) + { + $name = null; + + $lang = \Locale::getPrimaryLanguage($locale); + $script = \Locale::getScript($locale); + $region = \Locale::getRegion($locale); + $variants = \Locale::getAllVariants($locale); + + // Currently the only available variant is POSIX, which we don't want + // to include in the list + if (count($variants) > 0) { + return null; + } + + // Some languages are translated together with their region, + // i.e. "en_GB" is translated as "British English" + // we don't include these languages though because they mess up + // the name sorting + // $name = $this->langBundle->getLanguageName($displayLocale, $lang, $region); + + // Some languages are simply not translated + // Example: "az" (Azerbaijani) has no translation in "af" (Afrikaans) + if (null === ($name = $this->languageBundle->getLanguageName($lang, null, $displayLocale))) { + return null; + } + + // "as" (Assamese) has no "Variants" block + //if (!$langBundle->get('Variants')) { + // continue; + //} + + $extras = array(); + + // Discover the name of the script part of the locale + // i.e. in zh_Hans_MO, "Hans" is the script + if ($script) { + // Some scripts are not translated into every language + if (null === ($scriptName = $this->languageBundle->getScriptName($script, $lang, $displayLocale))) { + return null; + } + + $extras[] = $scriptName; + } + + // Discover the name of the region part of the locale + // i.e. in de_AT, "AT" is the region + if ($region) { + // Some regions are not translated into every language + if (null === ($regionName = $this->regionBundle->getCountryName($region, $displayLocale))) { + return null; + } + + $extras[] = $regionName; + } + + if (count($extras) > 0) { + // Remove any existing extras + // For example, in German, zh_Hans is "Chinesisch (vereinfacht)". + // The latter is the script part which is already included in the + // extras and will be appended again with the other extras. + if (preg_match('/^(.+)\s+\([^\)]+\)$/', $name, $matches)) { + $name = $matches[1]; + } + + $name .= ' ('.implode(', ', $extras).')'; + } + + return $name; + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/RegionBundleTransformationRule.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/RegionBundleTransformationRule.php new file mode 100644 index 00000000..52fdbed8 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/RegionBundleTransformationRule.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle\Transformer\Rule; + +use Symfony\Component\Intl\Intl; +use Symfony\Component\Intl\ResourceBundle\Transformer\CompilationContextInterface; +use Symfony\Component\Intl\ResourceBundle\Transformer\StubbingContextInterface; +use Symfony\Component\Intl\Util\IcuVersion; + +/** + * The rule for compiling the region bundle. + * + * @author Bernhard Schussek + */ +class RegionBundleTransformationRule implements TransformationRuleInterface +{ + /** + * {@inheritdoc} + */ + public function getBundleName() + { + return 'region'; + } + + /** + * {@inheritdoc} + */ + public function beforeCompile(CompilationContextInterface $context) + { + // The region data is contained in the locales bundle in ICU <= 4.2 + if (IcuVersion::compare($context->getIcuVersion(), '4.2', '<=', 1)) { + return $context->getSourceDir() . '/locales'; + } + + return $context->getSourceDir() . '/region'; + } + + /** + * {@inheritdoc} + */ + public function afterCompile(CompilationContextInterface $context) + { + } + + /** + * {@inheritdoc} + */ + public function beforeCreateStub(StubbingContextInterface $context) + { + return array( + 'Countries' => Intl::getRegionBundle()->getCountryNames('en'), + ); + } + + /** + * {@inheritdoc} + */ + public function afterCreateStub(StubbingContextInterface $context) + { + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/TransformationRuleInterface.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/TransformationRuleInterface.php new file mode 100644 index 00000000..3965e0d2 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/TransformationRuleInterface.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle\Transformer\Rule; + +use Symfony\Component\Intl\ResourceBundle\Transformer\CompilationContextInterface; +use Symfony\Component\Intl\ResourceBundle\Transformer\StubbingContextInterface; + +/** + * Contains instruction for compiling a resource bundle. + * + * @author Bernhard Schussek + */ +interface TransformationRuleInterface +{ + /** + * Returns the name of the compiled resource bundle. + * + * @return string The name of the bundle. + */ + public function getBundleName(); + + /** + * Runs instructions to be executed before compiling the sources of the + * resource bundle. + * + * @param CompilationContextInterface $context The contextual information of + * the compilation. + * + * @return string[] The source directories/files of the bundle. + */ + public function beforeCompile(CompilationContextInterface $context); + + /** + * Runs instructions to be executed after compiling the sources of the + * resource bundle. + * + * @param CompilationContextInterface $context The contextual information of + * the compilation. + */ + public function afterCompile(CompilationContextInterface $context); + + /** + * Runs instructions to be executed before creating the stub version of the + * resource bundle. + * + * @param StubbingContextInterface $context The contextual information of + * the compilation. + * + * @return mixed The data to include in the stub version. + */ + public function beforeCreateStub(StubbingContextInterface $context); + + /** + * Runs instructions to be executed after creating the stub version of the + * resource bundle. + * + * @param StubbingContextInterface $context The contextual information of + * the compilation. + */ + public function afterCreateStub(StubbingContextInterface $context); +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContext.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContext.php new file mode 100644 index 00000000..25ab68db --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContext.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle\Transformer; + +use Symfony\Component\Filesystem\Filesystem; + +/** + * @author Bernhard Schussek + */ +class StubbingContext implements StubbingContextInterface +{ + /** + * @var string + */ + private $binaryDir; + + /** + * @var string + */ + private $stubDir; + + /** + * @var Filesystem + */ + private $filesystem; + + /** + * @var string + */ + private $icuVersion; + + public function __construct($binaryDir, $stubDir, Filesystem $filesystem, $icuVersion) + { + $this->binaryDir = $binaryDir; + $this->stubDir = $stubDir; + $this->filesystem = $filesystem; + $this->icuVersion = $icuVersion; + } + + /** + * {@inheritdoc} + */ + public function getBinaryDir() + { + return $this->binaryDir; + } + + /** + * {@inheritdoc} + */ + public function getStubDir() + { + return $this->stubDir; + } + + /** + * {@inheritdoc} + */ + public function getFilesystem() + { + return $this->filesystem; + } + + /** + * {@inheritdoc} + */ + public function getIcuVersion() + { + return $this->icuVersion; + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContextInterface.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContextInterface.php new file mode 100644 index 00000000..dc492556 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContextInterface.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle\Transformer; + +/** + * @author Bernhard Schussek + */ +interface StubbingContextInterface +{ + /** + * Returns the directory where the binary resource bundles are stored. + * + * @return string An absolute path to a directory. + */ + public function getBinaryDir(); + + /** + * Returns the directory where the stub resource bundles are stored. + * + * @return string An absolute path to a directory. + */ + public function getStubDir(); + + /** + * Returns a tool for manipulating the filesystem. + * + * @return \Symfony\Component\Filesystem\Filesystem The filesystem manipulator. + */ + public function getFilesystem(); + + /** + * Returns the ICU version of the bundles being converted. + * + * @return string The ICU version string. + */ + public function getIcuVersion(); +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Util/ArrayAccessibleResourceBundle.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Util/ArrayAccessibleResourceBundle.php new file mode 100644 index 00000000..9a4cccb4 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Util/ArrayAccessibleResourceBundle.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle\Util; + +use Symfony\Component\Intl\Exception\BadMethodCallException; + +/** + * Work-around for a bug in PHP's \ResourceBundle implementation. + * + * More information can be found on https://bugs.php.net/bug.php?id=64356. + * This class can be removed once that bug is fixed. + * + * @author Bernhard Schussek + */ +class ArrayAccessibleResourceBundle implements \ArrayAccess, \IteratorAggregate, \Countable +{ + private $bundleImpl; + + public function __construct(\ResourceBundle $bundleImpl) + { + $this->bundleImpl = $bundleImpl; + } + + public function get($offset, $fallback = null) + { + $value = $this->bundleImpl->get($offset, $fallback); + + return $value instanceof \ResourceBundle ? new static($value) : $value; + } + + public function offsetExists($offset) + { + return null !== $this->bundleImpl[$offset]; + } + + public function offsetGet($offset) + { + return $this->get($offset); + } + + public function offsetSet($offset, $value) + { + throw new BadMethodCallException('Resource bundles cannot be modified.'); + } + + public function offsetUnset($offset) + { + throw new BadMethodCallException('Resource bundles cannot be modified.'); + } + + public function getIterator() + { + return $this->bundleImpl; + } + + public function count() + { + return $this->bundleImpl->count(); + } + + public function getErrorCode() + { + return $this->bundleImpl->getErrorCode(); + } + + public function getErrorMessage() + { + return $this->bundleImpl->getErrorMessage(); + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Util/RecursiveArrayAccess.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Util/RecursiveArrayAccess.php new file mode 100644 index 00000000..e1feaa2c --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Util/RecursiveArrayAccess.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle\Util; + +/** + * @author Bernhard Schussek + */ +class RecursiveArrayAccess +{ + public static function get($array, array $indices) + { + foreach ($indices as $index) { + if (!$array instanceof \ArrayAccess && !is_array($array)) { + return null; + } + + $array = $array[$index]; + } + + return $array; + } + + private function __construct() {} +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Util/RingBuffer.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Util/RingBuffer.php new file mode 100644 index 00000000..7ccbd1e7 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Util/RingBuffer.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle\Util; + +use Symfony\Component\Intl\Exception\OutOfBoundsException; + +/** + * Implements a ring buffer. + * + * A ring buffer is an array-like structure with a fixed size. If the buffer + * is full, the next written element overwrites the first bucket in the buffer, + * then the second and so on. + * + * @author Bernhard Schussek + */ +class RingBuffer implements \ArrayAccess +{ + private $values = array(); + + private $indices = array(); + + private $cursor = 0; + + private $size; + + public function __construct($size) + { + $this->size = $size; + } + + /** + * {@inheritdoc} + */ + public function offsetExists($key) + { + return isset($this->indices[$key]); + } + + /** + * {@inheritdoc} + */ + public function offsetGet($key) + { + if (!isset($this->indices[$key])) { + throw new OutOfBoundsException(sprintf( + 'The index "%s" does not exist.', + $key + )); + } + + return $this->values[$this->indices[$key]]; + } + + /** + * {@inheritdoc} + */ + public function offsetSet($key, $value) + { + if (false !== ($keyToRemove = array_search($this->cursor, $this->indices))) { + unset($this->indices[$keyToRemove]); + } + + $this->values[$this->cursor] = $value; + $this->indices[$key] = $this->cursor; + + $this->cursor = ($this->cursor + 1) % $this->size; + } + + /** + * {@inheritdoc} + */ + public function offsetUnset($key) + { + if (isset($this->indices[$key])) { + $this->values[$this->indices[$key]] = null; + unset($this->indices[$key]); + } + } +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Writer/BundleWriterInterface.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Writer/BundleWriterInterface.php new file mode 100644 index 00000000..cc3b9586 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Writer/BundleWriterInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle\Writer; + +/** + * Writes resource bundle files. + * + * @author Bernhard Schussek + */ +interface BundleWriterInterface +{ + /** + * Writes data to a resource bundle. + * + * @param string $path The path to the resource bundle. + * @param string $locale The locale to (over-)write. + * @param mixed $data The data to write. + */ + public function write($path, $locale, $data); +} diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Writer/PhpBundleWriter.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Writer/PhpBundleWriter.php new file mode 100644 index 00000000..d2688b49 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Writer/PhpBundleWriter.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\ResourceBundle\Writer; + +/** + * Writes .php resource bundles. + * + * @author Bernhard Schussek + */ +class PhpBundleWriter implements BundleWriterInterface +{ + /** + * {@inheritdoc} + */ + public function write($path, $locale, $data) + { + $template = <<