]> git.immae.eu Git - github/wallabag/wallabag.git/commitdiff
twig implementation
authorNicolas Lœuillet <nicolas.loeuillet@gmail.com>
Sat, 3 Aug 2013 17:26:54 +0000 (19:26 +0200)
committerNicolas Lœuillet <nicolas.loeuillet@gmail.com>
Sat, 3 Aug 2013 17:26:54 +0000 (19:26 +0200)
1418 files changed:
inc/3rdparty/Twig/Extensions/Autoloader.php [deleted file]
inc/3rdparty/Twig/Extensions/Extension/Debug.php [deleted file]
inc/3rdparty/Twig/Extensions/Extension/I18n.php [deleted file]
inc/3rdparty/Twig/Extensions/Extension/Intl.php [deleted file]
inc/3rdparty/Twig/Extensions/Extension/Text.php [deleted file]
inc/3rdparty/Twig/Extensions/Grammar.php [deleted file]
inc/3rdparty/Twig/Extensions/Grammar/Arguments.php [deleted file]
inc/3rdparty/Twig/Extensions/Grammar/Array.php [deleted file]
inc/3rdparty/Twig/Extensions/Grammar/Body.php [deleted file]
inc/3rdparty/Twig/Extensions/Grammar/Boolean.php [deleted file]
inc/3rdparty/Twig/Extensions/Grammar/Constant.php [deleted file]
inc/3rdparty/Twig/Extensions/Grammar/Expression.php [deleted file]
inc/3rdparty/Twig/Extensions/Grammar/Hash.php [deleted file]
inc/3rdparty/Twig/Extensions/Grammar/Number.php [deleted file]
inc/3rdparty/Twig/Extensions/Grammar/Optional.php [deleted file]
inc/3rdparty/Twig/Extensions/Grammar/Switch.php [deleted file]
inc/3rdparty/Twig/Extensions/Grammar/Tag.php [deleted file]
inc/3rdparty/Twig/Extensions/GrammarInterface.php [deleted file]
inc/3rdparty/Twig/Extensions/Node/Debug.php [deleted file]
inc/3rdparty/Twig/Extensions/Node/Trans.php [deleted file]
inc/3rdparty/Twig/Extensions/SimpleTokenParser.php [deleted file]
inc/3rdparty/Twig/Extensions/TokenParser/Debug.php [deleted file]
inc/3rdparty/Twig/Extensions/TokenParser/Trans.php [deleted file]
inc/3rdparty/Twig/Gettext/Extractor.php [deleted file]
inc/3rdparty/Twig/Gettext/Loader/Filesystem.php [deleted file]
inc/3rdparty/Twig/Gettext/Routing/Generator/UrlGenerator.php [deleted file]
inc/3rdparty/Twig/Gettext/Test/ExtractorTest.php [deleted file]
inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/empty.twig [deleted file]
inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/plural.twig [deleted file]
inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/singular.twig [deleted file]
inc/config.php
index.php
tpl/_head.twig
tpl/_top.twig
tpl/config.twig
tpl/css/knacss.css [moved from css/knacss.css with 100% similarity]
tpl/css/style-dark.css [moved from css/style-dark.css with 100% similarity]
tpl/css/style-light.css [moved from css/style-light.css with 100% similarity]
tpl/css/style.css [moved from css/style.css with 100% similarity]
tpl/entries.html [deleted file]
tpl/home.twig
tpl/img/apple-touch-icon-144x144-precomposed.png [moved from img/apple-touch-icon-144x144-precomposed.png with 100% similarity]
tpl/img/apple-touch-icon-72x72-precomposed.png [moved from img/apple-touch-icon-72x72-precomposed.png with 100% similarity]
tpl/img/apple-touch-icon.png [moved from img/apple-touch-icon.png with 100% similarity]
tpl/img/dark/checkmark-off.png [moved from img/dark/checkmark-off.png with 100% similarity]
tpl/img/dark/checkmark-on.png [moved from img/dark/checkmark-on.png with 100% similarity]
tpl/img/dark/down.png [moved from img/dark/down.png with 100% similarity]
tpl/img/dark/logo.png [moved from img/dark/logo.png with 100% similarity]
tpl/img/dark/remove.png [moved from img/dark/remove.png with 100% similarity]
tpl/img/dark/star-off.png [moved from img/dark/star-off.png with 100% similarity]
tpl/img/dark/star-on.png [moved from img/dark/star-on.png with 100% similarity]
tpl/img/dark/up.png [moved from img/dark/up.png with 100% similarity]
tpl/img/down.png [moved from img/down.png with 100% similarity]
tpl/img/favicon.ico [moved from img/favicon.ico with 100% similarity]
tpl/img/light/checkmark-off.png [moved from img/light/checkmark-off.png with 100% similarity]
tpl/img/light/checkmark-on.png [moved from img/light/checkmark-on.png with 100% similarity]
tpl/img/light/remove.png [moved from img/light/remove.png with 100% similarity]
tpl/img/light/star-off.png [moved from img/light/star-off.png with 100% similarity]
tpl/img/light/star-on.png [moved from img/light/star-on.png with 100% similarity]
tpl/img/logo.png [moved from img/logo.png with 100% similarity]
tpl/img/messages/close.png [moved from img/messages/close.png with 100% similarity]
tpl/img/messages/cross.png [moved from img/messages/cross.png with 100% similarity]
tpl/img/messages/help.png [moved from img/messages/help.png with 100% similarity]
tpl/img/messages/tick.png [moved from img/messages/tick.png with 100% similarity]
tpl/img/messages/warning.png [moved from img/messages/warning.png with 100% similarity]
tpl/img/up.png [moved from img/up.png with 100% similarity]
tpl/js/jquery-1.9.1.min.js [moved from js/jquery-1.9.1.min.js with 100% similarity]
tpl/js/jquery.masonry.min.js [moved from js/jquery.masonry.min.js with 100% similarity]
tpl/js/poche.js [moved from js/poche.js with 100% similarity]
tpl/login.twig
vendor/autoload.php [new file with mode: 0644]
vendor/bin/twig-gettext-extractor [new symlink]
vendor/composer/ClassLoader.php [new file with mode: 0644]
vendor/composer/autoload_classmap.php [new file with mode: 0644]
vendor/composer/autoload_files.php [new file with mode: 0644]
vendor/composer/autoload_namespaces.php [new file with mode: 0644]
vendor/composer/autoload_real.php [new file with mode: 0644]
vendor/composer/installed.json [new file with mode: 0644]
vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.gitignore [new file with mode: 0644]
vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/CHANGELOG.md [new file with mode: 0644]
vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php [new file with mode: 0644]
vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php [new file with mode: 0644]
vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Event.php [new file with mode: 0644]
vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcher.php [new file with mode: 0644]
vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcherInterface.php [new file with mode: 0644]
vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventSubscriberInterface.php [new file with mode: 0644]
vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/GenericEvent.php [new file with mode: 0644]
vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php [new file with mode: 0644]
vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/LICENSE [new file with mode: 0644]
vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/README.md [new file with mode: 0644]
vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php [new file with mode: 0644]
vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php [new file with mode: 0644]
vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventTest.php [new file with mode: 0644]
vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php [new file with mode: 0644]
vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php [new file with mode: 0644]
vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/composer.json [new file with mode: 0644]
vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/phpunit.xml.dist [new file with mode: 0644]
vendor/symfony/filesystem/Symfony/Component/Filesystem/.gitignore [new file with mode: 0644]
vendor/symfony/filesystem/Symfony/Component/Filesystem/CHANGELOG.md [new file with mode: 0644]
vendor/symfony/filesystem/Symfony/Component/Filesystem/Exception/ExceptionInterface.php [new file with mode: 0644]
vendor/symfony/filesystem/Symfony/Component/Filesystem/Exception/IOException.php [new file with mode: 0644]
vendor/symfony/filesystem/Symfony/Component/Filesystem/Filesystem.php [new file with mode: 0644]
vendor/symfony/filesystem/Symfony/Component/Filesystem/LICENSE [new file with mode: 0644]
vendor/symfony/filesystem/Symfony/Component/Filesystem/README.md [new file with mode: 0644]
vendor/symfony/filesystem/Symfony/Component/Filesystem/Tests/FilesystemTest.php [new file with mode: 0644]
vendor/symfony/filesystem/Symfony/Component/Filesystem/composer.json [new file with mode: 0644]
vendor/symfony/filesystem/Symfony/Component/Filesystem/phpunit.xml.dist [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/.gitignore [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/AbstractExtension.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/AbstractRendererEngine.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/AbstractType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/AbstractTypeExtension.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Button.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/ButtonBuilder.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/ButtonTypeInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/CHANGELOG.md [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/CallbackTransformer.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/ClickableInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/DataMapperInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/DataTransformerInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Exception/AlreadyBoundException.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Exception/AlreadySubmittedException.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Exception/BadMethodCallException.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Exception/ErrorMappingException.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Exception/ExceptionInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Exception/InvalidArgumentException.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Exception/InvalidConfigurationException.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Exception/LogicException.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Exception/OutOfBoundsException.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Exception/RuntimeException.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Exception/StringCastException.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Exception/TransformationFailedException.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Exception/UnexpectedTypeException.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceList.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceListInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/LazyChoiceList.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ObjectChoiceList.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/SimpleChoiceList.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/CoreExtension.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ArrayToPartsTransformer.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/BaseDateTimeTransformer.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/BooleanToStringTransformer.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ChoiceToBooleanArrayTransformer.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ChoiceToValueTransformer.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ChoicesToBooleanArrayTransformer.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ChoicesToValuesTransformer.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DataTransformerChain.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToRfc3339Transformer.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToTimestampTransformer.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ValueToDuplicatesTransformer.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/FixCheckboxInputListener.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/FixRadioInputListener.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/FixUrlProtocolListener.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/MergeCollectionListener.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/ResizeFormListener.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/TrimListener.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/BaseType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/BirthdayType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/ButtonType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/CollectionType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/CountryType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/DateType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/EmailType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/FileType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/FormType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/HiddenType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/IntegerType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/LanguageType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/LocaleType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/MoneyType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/NumberType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/PasswordType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/PercentType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/RadioType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/RepeatedType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/ResetType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/SearchType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/SubmitType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/TextType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/TextareaType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/TimeType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/UrlType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Core/View/ChoiceView.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/CsrfProvider/CsrfProviderInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/CsrfProvider/SessionCsrfProvider.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/HttpFoundation/EventListener/BindRequestListener.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationExtension.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationRequestHandler.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/HttpFoundation/Type/FormTypeHttpFoundationExtension.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Templating/TemplatingExtension.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Templating/TemplatingRendererEngine.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Constraints/Form.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/BaseValidatorExtension.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/RepeatedTypeValidatorExtension.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/SubmitTypeValidatorExtension.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Util/ServerParams.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/RelativePath.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapperInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPathIterator.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Form.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/FormBuilder.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/FormBuilderInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/FormConfigBuilder.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/FormConfigBuilderInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/FormConfigInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/FormError.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/FormEvent.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/FormEvents.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/FormExtensionInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/FormFactory.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/FormFactoryBuilder.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/FormFactoryBuilderInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/FormFactoryInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/FormInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/FormRegistry.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/FormRegistryInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/FormRenderer.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/FormRendererEngineInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/FormRendererInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/FormTypeExtensionInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/FormTypeGuesserChain.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/FormTypeGuesserInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/FormTypeInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/FormView.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Forms.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Guess/Guess.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Guess/TypeGuess.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Guess/ValueGuess.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/LICENSE [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/NativeRequestHandler.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/PreloadedExtension.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/README.md [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/RequestHandlerInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/ResolvedFormType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/ResolvedFormTypeFactory.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/ResolvedFormTypeFactoryInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/ResolvedFormTypeInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/config/validation.xml [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ar.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.bg.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ca.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.cs.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.da.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.de.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.el.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.en.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.es.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.et.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.eu.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.fa.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.fi.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.fr.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.gl.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.he.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.hr.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.hu.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.hy.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.id.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.it.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ja.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.lb.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.lt.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.lv.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.mn.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.nb.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.nl.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.pl.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.pt.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.pt_BR.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ro.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ru.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sk.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sl.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sr_Cyrl.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sr_Latn.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sv.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ua.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.zh_CN.xlf [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/ReversedTransformer.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/SubmitButton.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/SubmitButtonBuilder.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/SubmitButtonTypeInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Test/DeprecationErrorHandler.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Test/FormBuilderInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Test/FormIntegrationTestCase.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Test/FormInterface.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Test/FormPerformanceTestCase.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Test/TypeTestCase.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/AbstractExtensionTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/AbstractFormTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/AbstractLayoutTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/AbstractRequestHandlerTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/CompoundFormPerformanceTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/CompoundFormTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/ChoiceListTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/LazyChoiceListTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/ObjectChoiceListTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/SimpleChoiceListTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ArrayToPartsTransformerTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BooleanToStringTransformerTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoiceToValueTransformerTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoicesToValuesTransformerTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DataTransformerChainTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeTestCase.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToArrayTransformerTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToRfc3339TransformerTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToStringTransformerTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToTimestampTransformerTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/PercentToLocalizedStringTransformerTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ValueToDuplicatesTransformerTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixRadioInputListenerTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixUrlProtocolListenerTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/Fixtures/randomhash [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayObjectTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerCustomArrayObjectTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/ResizeFormListenerTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/TrimListenerTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/BaseTypeTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/ButtonTypeTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypePerformanceTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/CountryTypeTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/CurrencyTypeTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/LanguageTypeTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/LocaleTypeTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/MoneyTypeTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/PasswordTypeTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/SubmitTypeTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/TimezoneTypeTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/TypeTestCase.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/UrlTypeTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Csrf/CsrfProvider/DefaultCsrfProviderTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Csrf/CsrfProvider/SessionCsrfProviderTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/CsrfValidationListenerTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/HttpFoundation/EventListener/BindRequestListenerTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/EventListener/ValidationListenerTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/Type/TypeTestCase.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/Util/ServerParamsTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationPathTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/AlternatingRowType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/Author.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/AuthorType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/CustomArrayObject.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FixedDataTransformer.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FixedFilterListener.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooSubType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooSubTypeWithParentInstance.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooType.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooTypeBarExtension.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooTypeBazExtension.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/TestExtension.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/foo [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/FormBuilderTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/FormConfigTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/FormFactoryBuilderTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/FormFactoryTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/FormIntegrationTestCase.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/FormPerformanceTestCase.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/FormRegistryTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/FormRendererTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/Guess/GuessTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/NativeRequestHandlerTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/ResolvedFormTypeTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Tests/SimpleFormTest.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Util/FormUtil.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Util/InheritDataAwareIterator.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/Util/VirtualFormAwareIterator.php [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/composer.json [new file with mode: 0644]
vendor/symfony/form/Symfony/Component/Form/phpunit.xml.dist [new file with mode: 0644]
vendor/symfony/icu/Symfony/Component/Icu/.gitignore [new file with mode: 0644]
vendor/symfony/icu/Symfony/Component/Icu/IcuCurrencyBundle.php [new file with mode: 0644]
vendor/symfony/icu/Symfony/Component/Icu/IcuData.php [new file with mode: 0644]
vendor/symfony/icu/Symfony/Component/Icu/IcuLanguageBundle.php [new file with mode: 0644]
vendor/symfony/icu/Symfony/Component/Icu/IcuLocaleBundle.php [new file with mode: 0644]
vendor/symfony/icu/Symfony/Component/Icu/IcuRegionBundle.php [new file with mode: 0644]
vendor/symfony/icu/Symfony/Component/Icu/LICENSE [new file with mode: 0644]
vendor/symfony/icu/Symfony/Component/Icu/README.md [new file with mode: 0644]
vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en.php [new file with mode: 0644]
vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/en.php [new file with mode: 0644]
vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/en.php [new file with mode: 0644]
vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/en.php [new file with mode: 0644]
vendor/symfony/icu/Symfony/Component/Icu/Resources/data/version.txt [new file with mode: 0644]
vendor/symfony/icu/Symfony/Component/Icu/Tests/IcuIntegrationTest.php [new file with mode: 0644]
vendor/symfony/icu/Symfony/Component/Icu/composer.json [new file with mode: 0644]
vendor/symfony/icu/Symfony/Component/Icu/phpunit.xml.dist [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/.gitignore [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/CONTRIBUTING.md [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Collator/Collator.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/AmPmTransformer.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfWeekTransformer.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfYearTransformer.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/DayTransformer.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/FullTransformer.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1200Transformer.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1201Transformer.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2400Transformer.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2401Transformer.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/HourTransformer.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/MinuteTransformer.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/MonthTransformer.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/QuarterTransformer.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/SecondTransformer.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/TimeZoneTransformer.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Transformer.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/YearTransformer.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/IntlDateFormatter.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Exception/BadMethodCallException.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Exception/ExceptionInterface.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Exception/InvalidArgumentException.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Exception/MethodArgumentNotImplementedException.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Exception/MethodArgumentValueNotImplementedException.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Exception/MethodNotImplementedException.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Exception/NotImplementedException.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Exception/OutOfBoundsException.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Exception/RuntimeException.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Globals/IntlGlobals.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Intl.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/LICENSE [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Locale/Locale.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/README.md [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/AbstractBundle.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Compiler/BundleCompiler.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Compiler/BundleCompilerInterface.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/CurrencyBundle.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/CurrencyBundleInterface.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/LanguageBundle.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/LanguageBundleInterface.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/LocaleBundle.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/LocaleBundleInterface.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/AbstractBundleReader.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/BinaryBundleReader.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/BufferedBundleReader.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/BundleReaderInterface.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/PhpBundleReader.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReader.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReaderInterface.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/RegionBundle.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/RegionBundleInterface.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/ResourceBundleInterface.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/BundleTransformer.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContext.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContextInterface.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/CurrencyBundleTransformationRule.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LanguageBundleTransformationRule.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LocaleBundleTransformationRule.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/RegionBundleTransformationRule.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/TransformationRuleInterface.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContext.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContextInterface.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Util/ArrayAccessibleResourceBundle.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Util/RecursiveArrayAccess.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Util/RingBuffer.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Writer/BundleWriterInterface.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Writer/PhpBundleWriter.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Writer/TextBundleWriter.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/autoload.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/common.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/copy-stubs-to-component.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/create-stubs.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/icu-version.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/icu.ini [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/test-compat.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/update-icu-component.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/util/test-compat-helper.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Resources/stubs/Collator.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Resources/stubs/IntlDateFormatter.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Resources/stubs/Locale.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Resources/stubs/NumberFormatter.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Resources/stubs/functions.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/Collator/AbstractCollatorTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/Collator/CollatorTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/Collator/Verification/CollatorTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/DateFormatter/AbstractIntlDateFormatterTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/DateFormatter/IntlDateFormatterTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/DateFormatter/Verification/IntlDateFormatterTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/Globals/AbstractIntlGlobalsTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/Globals/IntlGlobalsTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/Globals/Verification/IntlGlobalsTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/Locale/AbstractLocaleTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/Locale/LocaleTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/Locale/Verification/LocaleTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/NumberFormatter/AbstractNumberFormatterTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/NumberFormatter/NumberFormatterTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/NumberFormatter/Verification/NumberFormatterTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/AbstractBundleTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/CurrencyBundleTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/LanguageBundleTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/LocaleBundleTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/AbstractBundleReaderTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/BinaryBundleReaderTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/NotAFile/en.php/.gitkeep [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/en.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/en.res [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/en.txt [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/PhpBundleReaderTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/StructuredBundleReaderTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/RegionBundleTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Util/RingBufferTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/en.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/en.res [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/en.txt [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/PhpBundleWriterTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/TextBundleWriterTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/Util/IcuVersionTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Tests/Util/VersionTest.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Util/IcuVersion.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Util/IntlTestHelper.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Util/SvnCommit.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Util/SvnRepository.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/Util/Version.php [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/composer.json [new file with mode: 0644]
vendor/symfony/intl/Symfony/Component/Intl/phpunit.xml.dist [new file with mode: 0644]
vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/.gitignore [new file with mode: 0644]
vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/ExceptionInterface.php [new file with mode: 0644]
vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/InvalidOptionsException.php [new file with mode: 0644]
vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/MissingOptionsException.php [new file with mode: 0644]
vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/OptionDefinitionException.php [new file with mode: 0644]
vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/LICENSE [new file with mode: 0644]
vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Options.php [new file with mode: 0644]
vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/OptionsResolver.php [new file with mode: 0644]
vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/OptionsResolverInterface.php [new file with mode: 0644]
vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/README.md [new file with mode: 0644]
vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php [new file with mode: 0644]
vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Tests/OptionsTest.php [new file with mode: 0644]
vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/composer.json [new file with mode: 0644]
vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/phpunit.xml.dist [new file with mode: 0644]
vendor/symfony/property-access/Symfony/Component/PropertyAccess/.gitattributes [new file with mode: 0644]
vendor/symfony/property-access/Symfony/Component/PropertyAccess/.gitignore [new file with mode: 0644]
vendor/symfony/property-access/Symfony/Component/PropertyAccess/CHANGELOG.md [new file with mode: 0644]
vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/ExceptionInterface.php [new file with mode: 0644]
vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/InvalidPropertyPathException.php [new file with mode: 0644]
vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/NoSuchPropertyException.php [new file with mode: 0644]
vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/OutOfBoundsException.php [new file with mode: 0644]
vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/RuntimeException.php [new file with mode: 0644]
vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.php [new file with mode: 0644]
vendor/symfony/property-access/Symfony/Component/PropertyAccess/LICENSE [new file with mode: 0644]
vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccess.php [new file with mode: 0644]
vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessor.php [new file with mode: 0644]
vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessorBuilder.php [new file with mode: 0644]
vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessorInterface.php [new file with mode: 0644]
vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPath.php [new file with mode: 0644]
vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathBuilder.php [new file with mode: 0644]
vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathInterface.php [new file with mode: 0644]
vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathIterator.php [new file with mode: 0644]
vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathIteratorInterface.php [new file with mode: 0644]
vendor/symfony/property-access/Symfony/Component/PropertyAccess/README.md [new file with mode: 0644]
vendor/symfony/property-access/Symfony/Component/PropertyAccess/StringUtil.php [new file with mode: 0644]
vendor/symfony/property-access/Symfony/Component/PropertyAccess/composer.json [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/.gitignore [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Annotation/Route.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/CHANGELOG.md [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/CompiledRoute.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Exception/ExceptionInterface.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Exception/InvalidParameterException.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Exception/MethodNotAllowedException.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Exception/MissingMandatoryParametersException.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Exception/ResourceNotFoundException.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Exception/RouteNotFoundException.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Generator/ConfigurableRequirementsInterface.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Generator/Dumper/GeneratorDumper.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Generator/Dumper/GeneratorDumperInterface.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Generator/UrlGenerator.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Generator/UrlGeneratorInterface.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/LICENSE [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Loader/AnnotationClassLoader.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Loader/AnnotationDirectoryLoader.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Loader/AnnotationFileLoader.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Loader/ClosureLoader.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Loader/PhpFileLoader.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Loader/XmlFileLoader.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Loader/YamlFileLoader.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Matcher/ApacheUrlMatcher.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/ApacheMatcherDumper.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/DumperCollection.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/DumperPrefixCollection.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/DumperRoute.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/MatcherDumper.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/MatcherDumperInterface.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Matcher/RedirectableUrlMatcher.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Matcher/RedirectableUrlMatcherInterface.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Matcher/RequestMatcherInterface.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Matcher/TraceableUrlMatcher.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Matcher/UrlMatcher.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Matcher/UrlMatcherInterface.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/README.md [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/RequestContext.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/RequestContextAwareInterface.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Route.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/RouteCollection.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/RouteCompiler.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/RouteCompilerInterface.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Router.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/RouterInterface.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Annotation/RouteTest.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/CompiledRouteTest.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/AbstractClass.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/BarClass.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/FooClass.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/CustomXmlFileLoader.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/RedirectableUrlMatcher.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/annotated.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.apache [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.apache [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/empty.yml [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/foo.xml [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/foo1.xml [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/incomplete.yml [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/missing_id.xml [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/missing_path.xml [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/namespaceprefix.xml [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonesense_resource_plus_path.yml [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonesense_type_without_resource.yml [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalid.xml [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalid.yml [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalid2.yml [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalidkeys.yml [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalidnode.xml [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalidroute.xml [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/special_route_name.yml [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validpattern.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validpattern.xml [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validpattern.yml [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validresource.xml [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validresource.yml [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/withdoctype.xml [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/AbstractAnnotationLoaderTest.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/AnnotationDirectoryLoaderTest.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/ClosureLoaderTest.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/ApacheUrlMatcherTest.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/Dumper/ApacheMatcherDumperTest.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/Dumper/DumperCollectionTest.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/Dumper/DumperPrefixCollectionTest.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/TraceableUrlMatcherTest.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/RouteCollectionTest.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/RouteCompilerTest.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/RouteTest.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/Tests/RouterTest.php [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/composer.json [new file with mode: 0644]
vendor/symfony/routing/Symfony/Component/Routing/phpunit.xml.dist [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/.gitignore [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/CHANGELOG.md [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Catalogue/AbstractOperation.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Catalogue/DiffOperation.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Catalogue/MergeOperation.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Catalogue/OperationInterface.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Dumper/CsvFileDumper.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Dumper/DumperInterface.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Dumper/FileDumper.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Dumper/IcuResFileDumper.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Dumper/IniFileDumper.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Dumper/MoFileDumper.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Dumper/PhpFileDumper.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Dumper/PoFileDumper.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Dumper/QtFileDumper.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Dumper/XliffFileDumper.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Dumper/YamlFileDumper.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Exception/ExceptionInterface.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Exception/InvalidResourceException.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Exception/NotFoundResourceException.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Extractor/ChainExtractor.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Extractor/ExtractorInterface.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/IdentityTranslator.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Interval.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/LICENSE [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Loader/ArrayLoader.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Loader/CsvFileLoader.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Loader/IcuDatFileLoader.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Loader/IcuResFileLoader.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Loader/IniFileLoader.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Loader/LoaderInterface.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Loader/MoFileLoader.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Loader/PhpFileLoader.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Loader/PoFileLoader.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Loader/QtFileLoader.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Loader/XliffFileLoader.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Loader/YamlFileLoader.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Loader/schema/dic/xliff-core/xliff-core-1.2-strict.xsd [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Loader/schema/dic/xliff-core/xml.xsd [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/MessageCatalogue.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/MessageCatalogueInterface.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/MessageSelector.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/MetadataAwareInterface.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/PluralizationRules.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/README.md [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/Catalogue/AbstractOperationTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/Catalogue/DiffOperationTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/Catalogue/MergeOperationTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/CsvFileDumperTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/IcuResFileDumperTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/IniFileDumperTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/MoFileDumperTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/PhpFileDumperTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/PoFileDumperTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/QtFileDumperTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/XliffFileDumperTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/YamlFileDumperTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/IdentityTranslatorTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/IntervalTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/CsvFileLoaderTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/IcuDatFileLoaderTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/IcuResFileLoaderTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/IniFileLoaderTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/LocalizedTestCase.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/MoFileLoaderTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/PhpFileLoaderTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/PoFileLoaderTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/QtFileLoaderTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/XliffFileLoaderTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/YamlFileLoaderTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/MessageCatalogueTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/MessageSelectorTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/PluralizationRulesTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/TranslatorTest.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty-translation.po [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty.csv [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty.ini [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty.mo [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty.po [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty.yml [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/encoding.xlf [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/invalid-xml-resources.xlf [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/non-valid.xlf [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/non-valid.yml [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/plurals.mo [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/plurals.po [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resname.xlf [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/corrupted/resources.dat [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/en.res [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/en.txt [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/fr.res [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/fr.txt [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/packagelist.txt [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/resources.dat [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/res/en.res [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources-clean.xlf [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.csv [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.ini [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.mo [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.po [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.ts [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.xlf [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.yml [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/valid.csv [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/withdoctype.xlf [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Translator.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/TranslatorInterface.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/Writer/TranslationWriter.php [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/composer.json [new file with mode: 0644]
vendor/symfony/translation/Symfony/Component/Translation/phpunit.xml.dist [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/.gitignore [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/CHANGELOG.md [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/CodeExtension.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/FormExtension.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/HttpKernelExtension.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/RoutingExtension.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/SecurityExtension.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/TranslationExtension.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/YamlExtension.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Form/TwigRenderer.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Form/TwigRendererEngine.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Form/TwigRendererEngineInterface.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Form/TwigRendererInterface.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/LICENSE [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/FormEnctypeNode.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/FormThemeNode.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/RenderBlockNode.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/SearchAndRenderBlockNode.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/TransDefaultDomainNode.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/TransNode.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/NodeVisitor/Scope.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/NodeVisitor/TranslationNodeVisitor.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/README.md [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/CodeExtensionTest.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubFilesystemLoader.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubTranslator.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/RoutingExtensionTest.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/child_label.html.twig [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/custom_widgets.html.twig [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/parent_label.html.twig [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/theme.html.twig [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/theme_extends.html.twig [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/theme_use.html.twig [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Node/SearchAndRenderBlockNodeTest.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/NodeVisitor/ScopeTest.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationDefaultDomainNodeVisitorTest.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationNodeVisitorTest.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/NodeVisitor/TwigNodeProvider.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/TestCase.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/TokenParser/FormThemeTokenParser.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/TokenParser/TransChoiceTokenParser.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/TokenParser/TransDefaultDomainTokenParser.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/TokenParser/TransTokenParser.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Translation/TwigExtractor.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/TwigEngine.php [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/composer.json [new file with mode: 0644]
vendor/symfony/twig-bridge/Symfony/Bridge/Twig/phpunit.xml.dist [new file with mode: 0644]
vendor/twig/extensions [new submodule]
vendor/twig/twig/.editorconfig [new file with mode: 0644]
vendor/twig/twig/.gitignore [new file with mode: 0644]
vendor/twig/twig/.travis.yml [new file with mode: 0644]
vendor/twig/twig/AUTHORS [new file with mode: 0644]
vendor/twig/twig/CHANGELOG [new file with mode: 0644]
vendor/twig/twig/LICENSE [new file with mode: 0644]
vendor/twig/twig/README.markdown [new file with mode: 0644]
vendor/twig/twig/composer.json [new file with mode: 0644]
vendor/twig/twig/doc/advanced.rst [new file with mode: 0644]
vendor/twig/twig/doc/advanced_legacy.rst [new file with mode: 0644]
vendor/twig/twig/doc/api.rst [new file with mode: 0644]
vendor/twig/twig/doc/coding_standards.rst [new file with mode: 0644]
vendor/twig/twig/doc/deprecated.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/abs.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/batch.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/capitalize.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/convert_encoding.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/date.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/date_modify.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/default.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/escape.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/first.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/format.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/index.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/join.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/json_encode.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/keys.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/last.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/length.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/lower.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/merge.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/nl2br.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/number_format.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/raw.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/replace.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/reverse.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/slice.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/sort.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/split.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/striptags.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/title.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/trim.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/upper.rst [new file with mode: 0644]
vendor/twig/twig/doc/filters/url_encode.rst [new file with mode: 0644]
vendor/twig/twig/doc/functions/attribute.rst [new file with mode: 0644]
vendor/twig/twig/doc/functions/block.rst [new file with mode: 0644]
vendor/twig/twig/doc/functions/constant.rst [new file with mode: 0644]
vendor/twig/twig/doc/functions/cycle.rst [new file with mode: 0644]
vendor/twig/twig/doc/functions/date.rst [new file with mode: 0644]
vendor/twig/twig/doc/functions/dump.rst [new file with mode: 0644]
vendor/twig/twig/doc/functions/include.rst [new file with mode: 0644]
vendor/twig/twig/doc/functions/index.rst [new file with mode: 0644]
vendor/twig/twig/doc/functions/parent.rst [new file with mode: 0644]
vendor/twig/twig/doc/functions/random.rst [new file with mode: 0644]
vendor/twig/twig/doc/functions/range.rst [new file with mode: 0644]
vendor/twig/twig/doc/functions/template_from_string.rst [new file with mode: 0644]
vendor/twig/twig/doc/index.rst [new file with mode: 0644]
vendor/twig/twig/doc/internals.rst [new file with mode: 0644]
vendor/twig/twig/doc/intro.rst [new file with mode: 0644]
vendor/twig/twig/doc/recipes.rst [new file with mode: 0644]
vendor/twig/twig/doc/tags/autoescape.rst [new file with mode: 0644]
vendor/twig/twig/doc/tags/block.rst [new file with mode: 0644]
vendor/twig/twig/doc/tags/do.rst [new file with mode: 0644]
vendor/twig/twig/doc/tags/embed.rst [new file with mode: 0644]
vendor/twig/twig/doc/tags/extends.rst [new file with mode: 0644]
vendor/twig/twig/doc/tags/filter.rst [new file with mode: 0644]
vendor/twig/twig/doc/tags/flush.rst [new file with mode: 0644]
vendor/twig/twig/doc/tags/for.rst [new file with mode: 0644]
vendor/twig/twig/doc/tags/from.rst [new file with mode: 0644]
vendor/twig/twig/doc/tags/if.rst [new file with mode: 0644]
vendor/twig/twig/doc/tags/import.rst [new file with mode: 0644]
vendor/twig/twig/doc/tags/include.rst [new file with mode: 0644]
vendor/twig/twig/doc/tags/index.rst [new file with mode: 0644]
vendor/twig/twig/doc/tags/macro.rst [new file with mode: 0644]
vendor/twig/twig/doc/tags/sandbox.rst [new file with mode: 0644]
vendor/twig/twig/doc/tags/set.rst [new file with mode: 0644]
vendor/twig/twig/doc/tags/spaceless.rst [new file with mode: 0644]
vendor/twig/twig/doc/tags/use.rst [new file with mode: 0644]
vendor/twig/twig/doc/tags/verbatim.rst [new file with mode: 0644]
vendor/twig/twig/doc/templates.rst [new file with mode: 0644]
vendor/twig/twig/doc/tests/constant.rst [new file with mode: 0644]
vendor/twig/twig/doc/tests/defined.rst [new file with mode: 0644]
vendor/twig/twig/doc/tests/divisibleby.rst [new file with mode: 0644]
vendor/twig/twig/doc/tests/empty.rst [new file with mode: 0644]
vendor/twig/twig/doc/tests/even.rst [new file with mode: 0644]
vendor/twig/twig/doc/tests/index.rst [new file with mode: 0644]
vendor/twig/twig/doc/tests/iterable.rst [new file with mode: 0644]
vendor/twig/twig/doc/tests/null.rst [new file with mode: 0644]
vendor/twig/twig/doc/tests/odd.rst [new file with mode: 0644]
vendor/twig/twig/doc/tests/sameas.rst [new file with mode: 0644]
vendor/twig/twig/ext/twig/.gitignore [new file with mode: 0644]
vendor/twig/twig/ext/twig/LICENSE [new file with mode: 0644]
vendor/twig/twig/ext/twig/config.m4 [new file with mode: 0644]
vendor/twig/twig/ext/twig/config.w32 [new file with mode: 0644]
vendor/twig/twig/ext/twig/php_twig.h [new file with mode: 0644]
vendor/twig/twig/ext/twig/twig.c [new file with mode: 0644]
vendor/twig/twig/lib/Twig/Autoloader.php [moved from inc/3rdparty/Twig/Autoloader.php with 100% similarity]
vendor/twig/twig/lib/Twig/Compiler.php [moved from inc/3rdparty/Twig/Compiler.php with 100% similarity]
vendor/twig/twig/lib/Twig/CompilerInterface.php [moved from inc/3rdparty/Twig/CompilerInterface.php with 100% similarity]
vendor/twig/twig/lib/Twig/Environment.php [moved from inc/3rdparty/Twig/Environment.php with 99% similarity]
vendor/twig/twig/lib/Twig/Error.php [moved from inc/3rdparty/Twig/Error.php with 94% similarity]
vendor/twig/twig/lib/Twig/Error/Loader.php [moved from inc/3rdparty/Twig/Error/Loader.php with 100% similarity]
vendor/twig/twig/lib/Twig/Error/Runtime.php [moved from inc/3rdparty/Twig/Error/Runtime.php with 100% similarity]
vendor/twig/twig/lib/Twig/Error/Syntax.php [moved from inc/3rdparty/Twig/Error/Syntax.php with 100% similarity]
vendor/twig/twig/lib/Twig/ExistsLoaderInterface.php [moved from inc/3rdparty/Twig/ExistsLoaderInterface.php with 100% similarity]
vendor/twig/twig/lib/Twig/ExpressionParser.php [moved from inc/3rdparty/Twig/ExpressionParser.php with 100% similarity]
vendor/twig/twig/lib/Twig/Extension.php [moved from inc/3rdparty/Twig/Extension.php with 100% similarity]
vendor/twig/twig/lib/Twig/Extension/Core.php [moved from inc/3rdparty/Twig/Extension/Core.php with 100% similarity]
vendor/twig/twig/lib/Twig/Extension/Debug.php [moved from inc/3rdparty/Twig/Extension/Debug.php with 100% similarity]
vendor/twig/twig/lib/Twig/Extension/Escaper.php [moved from inc/3rdparty/Twig/Extension/Escaper.php with 100% similarity]
vendor/twig/twig/lib/Twig/Extension/Optimizer.php [moved from inc/3rdparty/Twig/Extension/Optimizer.php with 100% similarity]
vendor/twig/twig/lib/Twig/Extension/Sandbox.php [moved from inc/3rdparty/Twig/Extension/Sandbox.php with 100% similarity]
vendor/twig/twig/lib/Twig/Extension/Staging.php [moved from inc/3rdparty/Twig/Extension/Staging.php with 100% similarity]
vendor/twig/twig/lib/Twig/Extension/StringLoader.php [moved from inc/3rdparty/Twig/Extension/StringLoader.php with 100% similarity]
vendor/twig/twig/lib/Twig/ExtensionInterface.php [moved from inc/3rdparty/Twig/ExtensionInterface.php with 100% similarity]
vendor/twig/twig/lib/Twig/Filter.php [moved from inc/3rdparty/Twig/Filter.php with 100% similarity]
vendor/twig/twig/lib/Twig/Filter/Function.php [moved from inc/3rdparty/Twig/Filter/Function.php with 100% similarity]
vendor/twig/twig/lib/Twig/Filter/Method.php [moved from inc/3rdparty/Twig/Filter/Method.php with 100% similarity]
vendor/twig/twig/lib/Twig/Filter/Node.php [moved from inc/3rdparty/Twig/Filter/Node.php with 100% similarity]
vendor/twig/twig/lib/Twig/FilterCallableInterface.php [moved from inc/3rdparty/Twig/FilterCallableInterface.php with 100% similarity]
vendor/twig/twig/lib/Twig/FilterInterface.php [moved from inc/3rdparty/Twig/FilterInterface.php with 100% similarity]
vendor/twig/twig/lib/Twig/Function.php [moved from inc/3rdparty/Twig/Function.php with 100% similarity]
vendor/twig/twig/lib/Twig/Function/Function.php [moved from inc/3rdparty/Twig/Function/Function.php with 100% similarity]
vendor/twig/twig/lib/Twig/Function/Method.php [moved from inc/3rdparty/Twig/Function/Method.php with 100% similarity]
vendor/twig/twig/lib/Twig/Function/Node.php [moved from inc/3rdparty/Twig/Function/Node.php with 100% similarity]
vendor/twig/twig/lib/Twig/FunctionCallableInterface.php [moved from inc/3rdparty/Twig/FunctionCallableInterface.php with 100% similarity]
vendor/twig/twig/lib/Twig/FunctionInterface.php [moved from inc/3rdparty/Twig/FunctionInterface.php with 100% similarity]
vendor/twig/twig/lib/Twig/Lexer.php [moved from inc/3rdparty/Twig/Lexer.php with 100% similarity]
vendor/twig/twig/lib/Twig/LexerInterface.php [moved from inc/3rdparty/Twig/LexerInterface.php with 100% similarity]
vendor/twig/twig/lib/Twig/Loader/Array.php [moved from inc/3rdparty/Twig/Loader/Array.php with 100% similarity]
vendor/twig/twig/lib/Twig/Loader/Chain.php [moved from inc/3rdparty/Twig/Loader/Chain.php with 100% similarity]
vendor/twig/twig/lib/Twig/Loader/Filesystem.php [moved from inc/3rdparty/Twig/Loader/Filesystem.php with 92% similarity]
vendor/twig/twig/lib/Twig/Loader/String.php [moved from inc/3rdparty/Twig/Loader/String.php with 100% similarity]
vendor/twig/twig/lib/Twig/LoaderInterface.php [moved from inc/3rdparty/Twig/LoaderInterface.php with 100% similarity]
vendor/twig/twig/lib/Twig/Markup.php [moved from inc/3rdparty/Twig/Markup.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node.php [moved from inc/3rdparty/Twig/Node.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/AutoEscape.php [moved from inc/3rdparty/Twig/Node/AutoEscape.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Block.php [moved from inc/3rdparty/Twig/Node/Block.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/BlockReference.php [moved from inc/3rdparty/Twig/Node/BlockReference.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Body.php [moved from inc/3rdparty/Twig/Node/Body.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Do.php [moved from inc/3rdparty/Twig/Node/Do.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Embed.php [moved from inc/3rdparty/Twig/Node/Embed.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression.php [moved from inc/3rdparty/Twig/Node/Expression.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Array.php [moved from inc/3rdparty/Twig/Node/Expression/Array.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/AssignName.php [moved from inc/3rdparty/Twig/Node/Expression/AssignName.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Binary.php [moved from inc/3rdparty/Twig/Node/Expression/Binary.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Add.php [moved from inc/3rdparty/Twig/Node/Expression/Binary/Add.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Binary/And.php [moved from inc/3rdparty/Twig/Node/Expression/Binary/And.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseAnd.php [moved from inc/3rdparty/Twig/Node/Expression/Binary/BitwiseAnd.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseOr.php [moved from inc/3rdparty/Twig/Node/Expression/Binary/BitwiseOr.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseXor.php [moved from inc/3rdparty/Twig/Node/Expression/Binary/BitwiseXor.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Concat.php [moved from inc/3rdparty/Twig/Node/Expression/Binary/Concat.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Div.php [moved from inc/3rdparty/Twig/Node/Expression/Binary/Div.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Equal.php [moved from inc/3rdparty/Twig/Node/Expression/Binary/Equal.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Binary/FloorDiv.php [moved from inc/3rdparty/Twig/Node/Expression/Binary/FloorDiv.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Greater.php [moved from inc/3rdparty/Twig/Node/Expression/Binary/Greater.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Binary/GreaterEqual.php [moved from inc/3rdparty/Twig/Node/Expression/Binary/GreaterEqual.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Binary/In.php [moved from inc/3rdparty/Twig/Node/Expression/Binary/In.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Less.php [moved from inc/3rdparty/Twig/Node/Expression/Binary/Less.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Binary/LessEqual.php [moved from inc/3rdparty/Twig/Node/Expression/Binary/LessEqual.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mod.php [moved from inc/3rdparty/Twig/Node/Expression/Binary/Mod.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mul.php [moved from inc/3rdparty/Twig/Node/Expression/Binary/Mul.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotEqual.php [moved from inc/3rdparty/Twig/Node/Expression/Binary/NotEqual.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotIn.php [moved from inc/3rdparty/Twig/Node/Expression/Binary/NotIn.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Or.php [moved from inc/3rdparty/Twig/Node/Expression/Binary/Or.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Power.php [moved from inc/3rdparty/Twig/Node/Expression/Binary/Power.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Range.php [moved from inc/3rdparty/Twig/Node/Expression/Binary/Range.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Sub.php [moved from inc/3rdparty/Twig/Node/Expression/Binary/Sub.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/BlockReference.php [moved from inc/3rdparty/Twig/Node/Expression/BlockReference.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Call.php [moved from inc/3rdparty/Twig/Node/Expression/Call.php with 95% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Conditional.php [moved from inc/3rdparty/Twig/Node/Expression/Conditional.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Constant.php [moved from inc/3rdparty/Twig/Node/Expression/Constant.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/ExtensionReference.php [moved from inc/3rdparty/Twig/Node/Expression/ExtensionReference.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Filter.php [moved from inc/3rdparty/Twig/Node/Expression/Filter.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Filter/Default.php [moved from inc/3rdparty/Twig/Node/Expression/Filter/Default.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Function.php [moved from inc/3rdparty/Twig/Node/Expression/Function.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/GetAttr.php [moved from inc/3rdparty/Twig/Node/Expression/GetAttr.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/MethodCall.php [moved from inc/3rdparty/Twig/Node/Expression/MethodCall.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Name.php [moved from inc/3rdparty/Twig/Node/Expression/Name.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Parent.php [moved from inc/3rdparty/Twig/Node/Expression/Parent.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/TempName.php [moved from inc/3rdparty/Twig/Node/Expression/TempName.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Test.php [moved from inc/3rdparty/Twig/Node/Expression/Test.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Test/Constant.php [moved from inc/3rdparty/Twig/Node/Expression/Test/Constant.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Test/Defined.php [moved from inc/3rdparty/Twig/Node/Expression/Test/Defined.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Test/Divisibleby.php [moved from inc/3rdparty/Twig/Node/Expression/Test/Divisibleby.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Test/Even.php [moved from inc/3rdparty/Twig/Node/Expression/Test/Even.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Test/Null.php [moved from inc/3rdparty/Twig/Node/Expression/Test/Null.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Test/Odd.php [moved from inc/3rdparty/Twig/Node/Expression/Test/Odd.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Test/Sameas.php [moved from inc/3rdparty/Twig/Node/Expression/Test/Sameas.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Unary.php [moved from inc/3rdparty/Twig/Node/Expression/Unary.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Unary/Neg.php [moved from inc/3rdparty/Twig/Node/Expression/Unary/Neg.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Unary/Not.php [moved from inc/3rdparty/Twig/Node/Expression/Unary/Not.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Expression/Unary/Pos.php [moved from inc/3rdparty/Twig/Node/Expression/Unary/Pos.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Flush.php [moved from inc/3rdparty/Twig/Node/Flush.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/For.php [moved from inc/3rdparty/Twig/Node/For.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/ForLoop.php [moved from inc/3rdparty/Twig/Node/ForLoop.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/If.php [moved from inc/3rdparty/Twig/Node/If.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Import.php [moved from inc/3rdparty/Twig/Node/Import.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Include.php [moved from inc/3rdparty/Twig/Node/Include.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Macro.php [moved from inc/3rdparty/Twig/Node/Macro.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Module.php [moved from inc/3rdparty/Twig/Node/Module.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Print.php [moved from inc/3rdparty/Twig/Node/Print.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Sandbox.php [moved from inc/3rdparty/Twig/Node/Sandbox.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/SandboxedModule.php [moved from inc/3rdparty/Twig/Node/SandboxedModule.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/SandboxedPrint.php [moved from inc/3rdparty/Twig/Node/SandboxedPrint.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Set.php [moved from inc/3rdparty/Twig/Node/Set.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/SetTemp.php [moved from inc/3rdparty/Twig/Node/SetTemp.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Spaceless.php [moved from inc/3rdparty/Twig/Node/Spaceless.php with 100% similarity]
vendor/twig/twig/lib/Twig/Node/Text.php [moved from inc/3rdparty/Twig/Node/Text.php with 100% similarity]
vendor/twig/twig/lib/Twig/NodeInterface.php [moved from inc/3rdparty/Twig/NodeInterface.php with 100% similarity]
vendor/twig/twig/lib/Twig/NodeOutputInterface.php [moved from inc/3rdparty/Twig/NodeOutputInterface.php with 100% similarity]
vendor/twig/twig/lib/Twig/NodeTraverser.php [moved from inc/3rdparty/Twig/NodeTraverser.php with 100% similarity]
vendor/twig/twig/lib/Twig/NodeVisitor/Escaper.php [moved from inc/3rdparty/Twig/NodeVisitor/Escaper.php with 100% similarity]
vendor/twig/twig/lib/Twig/NodeVisitor/Optimizer.php [moved from inc/3rdparty/Twig/NodeVisitor/Optimizer.php with 100% similarity]
vendor/twig/twig/lib/Twig/NodeVisitor/SafeAnalysis.php [moved from inc/3rdparty/Twig/NodeVisitor/SafeAnalysis.php with 100% similarity]
vendor/twig/twig/lib/Twig/NodeVisitor/Sandbox.php [moved from inc/3rdparty/Twig/NodeVisitor/Sandbox.php with 100% similarity]
vendor/twig/twig/lib/Twig/NodeVisitorInterface.php [moved from inc/3rdparty/Twig/NodeVisitorInterface.php with 100% similarity]
vendor/twig/twig/lib/Twig/Parser.php [moved from inc/3rdparty/Twig/Parser.php with 100% similarity]
vendor/twig/twig/lib/Twig/ParserInterface.php [moved from inc/3rdparty/Twig/ParserInterface.php with 100% similarity]
vendor/twig/twig/lib/Twig/Sandbox/SecurityError.php [moved from inc/3rdparty/Twig/Sandbox/SecurityError.php with 100% similarity]
vendor/twig/twig/lib/Twig/Sandbox/SecurityPolicy.php [moved from inc/3rdparty/Twig/Sandbox/SecurityPolicy.php with 100% similarity]
vendor/twig/twig/lib/Twig/Sandbox/SecurityPolicyInterface.php [moved from inc/3rdparty/Twig/Sandbox/SecurityPolicyInterface.php with 100% similarity]
vendor/twig/twig/lib/Twig/SimpleFilter.php [moved from inc/3rdparty/Twig/SimpleFilter.php with 100% similarity]
vendor/twig/twig/lib/Twig/SimpleFunction.php [moved from inc/3rdparty/Twig/SimpleFunction.php with 100% similarity]
vendor/twig/twig/lib/Twig/SimpleTest.php [moved from inc/3rdparty/Twig/SimpleTest.php with 100% similarity]
vendor/twig/twig/lib/Twig/Template.php [moved from inc/3rdparty/Twig/Template.php with 100% similarity]
vendor/twig/twig/lib/Twig/TemplateInterface.php [moved from inc/3rdparty/Twig/TemplateInterface.php with 100% similarity]
vendor/twig/twig/lib/Twig/Test.php [moved from inc/3rdparty/Twig/Test.php with 100% similarity]
vendor/twig/twig/lib/Twig/Test/Function.php [moved from inc/3rdparty/Twig/Test/Function.php with 100% similarity]
vendor/twig/twig/lib/Twig/Test/IntegrationTestCase.php [moved from inc/3rdparty/Twig/Test/IntegrationTestCase.php with 100% similarity]
vendor/twig/twig/lib/Twig/Test/Method.php [moved from inc/3rdparty/Twig/Test/Method.php with 100% similarity]
vendor/twig/twig/lib/Twig/Test/Node.php [moved from inc/3rdparty/Twig/Test/Node.php with 100% similarity]
vendor/twig/twig/lib/Twig/Test/NodeTestCase.php [moved from inc/3rdparty/Twig/Test/NodeTestCase.php with 100% similarity]
vendor/twig/twig/lib/Twig/TestCallableInterface.php [moved from inc/3rdparty/Twig/TestCallableInterface.php with 100% similarity]
vendor/twig/twig/lib/Twig/TestInterface.php [moved from inc/3rdparty/Twig/TestInterface.php with 100% similarity]
vendor/twig/twig/lib/Twig/Token.php [moved from inc/3rdparty/Twig/Token.php with 100% similarity]
vendor/twig/twig/lib/Twig/TokenParser.php [moved from inc/3rdparty/Twig/TokenParser.php with 100% similarity]
vendor/twig/twig/lib/Twig/TokenParser/AutoEscape.php [moved from inc/3rdparty/Twig/TokenParser/AutoEscape.php with 100% similarity]
vendor/twig/twig/lib/Twig/TokenParser/Block.php [moved from inc/3rdparty/Twig/TokenParser/Block.php with 100% similarity]
vendor/twig/twig/lib/Twig/TokenParser/Do.php [moved from inc/3rdparty/Twig/TokenParser/Do.php with 100% similarity]
vendor/twig/twig/lib/Twig/TokenParser/Embed.php [moved from inc/3rdparty/Twig/TokenParser/Embed.php with 100% similarity]
vendor/twig/twig/lib/Twig/TokenParser/Extends.php [moved from inc/3rdparty/Twig/TokenParser/Extends.php with 100% similarity]
vendor/twig/twig/lib/Twig/TokenParser/Filter.php [moved from inc/3rdparty/Twig/TokenParser/Filter.php with 100% similarity]
vendor/twig/twig/lib/Twig/TokenParser/Flush.php [moved from inc/3rdparty/Twig/TokenParser/Flush.php with 100% similarity]
vendor/twig/twig/lib/Twig/TokenParser/For.php [moved from inc/3rdparty/Twig/TokenParser/For.php with 100% similarity]
vendor/twig/twig/lib/Twig/TokenParser/From.php [moved from inc/3rdparty/Twig/TokenParser/From.php with 100% similarity]
vendor/twig/twig/lib/Twig/TokenParser/If.php [moved from inc/3rdparty/Twig/TokenParser/If.php with 100% similarity]
vendor/twig/twig/lib/Twig/TokenParser/Import.php [moved from inc/3rdparty/Twig/TokenParser/Import.php with 100% similarity]
vendor/twig/twig/lib/Twig/TokenParser/Include.php [moved from inc/3rdparty/Twig/TokenParser/Include.php with 100% similarity]
vendor/twig/twig/lib/Twig/TokenParser/Macro.php [moved from inc/3rdparty/Twig/TokenParser/Macro.php with 100% similarity]
vendor/twig/twig/lib/Twig/TokenParser/Sandbox.php [moved from inc/3rdparty/Twig/TokenParser/Sandbox.php with 100% similarity]
vendor/twig/twig/lib/Twig/TokenParser/Set.php [moved from inc/3rdparty/Twig/TokenParser/Set.php with 100% similarity]
vendor/twig/twig/lib/Twig/TokenParser/Spaceless.php [moved from inc/3rdparty/Twig/TokenParser/Spaceless.php with 100% similarity]
vendor/twig/twig/lib/Twig/TokenParser/Use.php [moved from inc/3rdparty/Twig/TokenParser/Use.php with 100% similarity]
vendor/twig/twig/lib/Twig/TokenParserBroker.php [moved from inc/3rdparty/Twig/TokenParserBroker.php with 100% similarity]
vendor/twig/twig/lib/Twig/TokenParserBrokerInterface.php [moved from inc/3rdparty/Twig/TokenParserBrokerInterface.php with 100% similarity]
vendor/twig/twig/lib/Twig/TokenParserInterface.php [moved from inc/3rdparty/Twig/TokenParserInterface.php with 100% similarity]
vendor/twig/twig/lib/Twig/TokenStream.php [moved from inc/3rdparty/Twig/TokenStream.php with 100% similarity]
vendor/twig/twig/phpunit.xml.dist [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/AutoloaderTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/CompilerTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/EnvironmentTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/ErrorTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/ExpressionParserTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Extension/CoreTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Extension/SandboxTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/FileCachingTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/errors/base.html [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/errors/index.html [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/unclosed_tag.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/array.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/array_call.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/binary.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/bitwise.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/comparison.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/dotdot.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/grouping.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/literals.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/magic_call.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/method_call.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/postfix.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/strings.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/ternary_operator.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/ternary_operator_noelse.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/ternary_operator_nothen.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/unary.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/unary_precedence.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/abs.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch_float.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch_with_empty_fill.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch_with_fill.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/convert_encoding.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format_interval.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_interval.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_modify.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_namedargs.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/default.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/dynamic_filter.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape_non_supported_charset.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/first.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/force_escape.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/format.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/join.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/json_encode.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/last.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/length.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/length_utf8.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/merge.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/nl2br.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/number_format.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/number_format_default.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/replace.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/reverse.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/slice.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/sort.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/special_chars.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/split.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/trim.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/filters/urlencode.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/functions/attribute.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/functions/block.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/functions/constant.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/functions/cycle.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/functions/date.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/functions/date_namedargs.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/functions/dump.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/functions/dump_array.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/functions/dynamic_function.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/assignment.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/autoescaping.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/basic.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/expression.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/ignore_missing.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/missing.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/missing_nested.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/sandbox.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/template_instance.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/templates_as_array.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/with_context.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/with_variables.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/functions/range.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/functions/special_chars.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/functions/template_from_string.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/macros/default_values.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/macros/nested_calls.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/macros/reserved_variables.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/macros/simple.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/macros/with_filters.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/regression/empty_token.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/regression/simple_xml_element.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/regression/strings_like_numbers.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/basic.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/blocks.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/double_escaping.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/functions.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/literal.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/nested.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/objects.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/raw.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/strategy.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/type.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_filters.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_filters_arguments.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_pre_escape_filters.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_preserves_safety_filters.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/basic.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/block_unique_name.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/special_chars.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/basic.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/error_line.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/multiple.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/nested.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/with_extends.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/basic.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/json_encode.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/multiple.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/nested.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/with_for_tag.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/with_if_tag.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/condition.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/context.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/else.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/inner_variables.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/keys.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/keys_and_values.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_context.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_context_local.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_not_defined.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_not_defined_cond.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/nested_else.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/objects.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/objects_countable.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/recursive.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/values.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/from.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/if/basic.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/if/expression.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/basic.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/expression.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/ignore_missing.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/missing.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/missing_nested.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/only.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/template_instance.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/templates_as_array.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/with_variables.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/basic.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/conditional.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/dynamic.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/empty.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/extends_as_array.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/multiple.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/nested_blocks.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/nested_blocks_parent_only.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/nested_inheritance.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_change.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_in_a_block.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_isolation.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_nested.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends_but_traits.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/template_instance.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/use.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/basic.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/endmacro_name.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/external.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/global.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/self_import.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/special_chars.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/basic.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/mixed_usage_with_raw.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/whitespace_control.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/not_valid1.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/not_valid2.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/simple.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/basic.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/capture-empty.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/capture.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/expression.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/spaceless/simple.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/special_chars.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/trim_block.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/aliases.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/basic.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/deep.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/deep_empty.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/multiple.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/multiple_aliases.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/basic.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/mixed_usage_with_raw.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/whitespace_control.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tests/array.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tests/constant.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tests/empty.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tests/even.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tests/in.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tests/in_with_objects.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tests/iterable.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Fixtures/tests/odd.test [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/IntegrationTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/LexerTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Loader/ArrayTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Loader/ChainTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Loader/FilesystemTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named/index.html [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named_bis/index.html [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named_final/index.html [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named_ter/index.html [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal/index.html [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal_bis/index.html [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal_final/index.html [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal_ter/index.html [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/NativeExtensionTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/AutoEscapeTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/BlockReferenceTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/BlockTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/DoTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/ArrayTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/AssignNameTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/AddTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/AndTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/ConcatTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/DivTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/FloorDivTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/ModTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/MulTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/OrTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/SubTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/CallTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/ConditionalTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/ConstantTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/FilterTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/FunctionTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/GetAttrTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/NameTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/PHP53/FilterInclude.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/PHP53/FunctionInclude.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/PHP53/TestInclude.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/ParentTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/TestTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/Unary/NegTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/Unary/NotTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/Expression/Unary/PosTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/ForTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/IfTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/ImportTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/IncludeTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/MacroTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/ModuleTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/PrintTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/SandboxTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/SandboxedModuleTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/SandboxedPrintTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/SetTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/SpacelessTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/Node/TextTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/NodeVisitor/OptimizerTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/ParserTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/TemplateTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/TokenStreamTest.php [new file with mode: 0644]
vendor/twig/twig/test/Twig/Tests/escapingTest.php [new file with mode: 0644]
vendor/twig/twig/test/bootstrap.php [new file with mode: 0644]
vendor/umpirsky/twig-gettext-extractor/.gitignore [new file with mode: 0644]
vendor/umpirsky/twig-gettext-extractor/.travis.yml [new file with mode: 0644]
vendor/umpirsky/twig-gettext-extractor/LICENSE [new file with mode: 0644]
vendor/umpirsky/twig-gettext-extractor/README.md [new file with mode: 0644]
vendor/umpirsky/twig-gettext-extractor/Twig/Gettext/Extractor.php [moved from inc/3rdparty/Twig/Extensions/Gettext/Extractor.php with 100% similarity]
vendor/umpirsky/twig-gettext-extractor/Twig/Gettext/Loader/Filesystem.php [moved from inc/3rdparty/Twig/Extensions/Gettext/Loader/Filesystem.php with 100% similarity]
vendor/umpirsky/twig-gettext-extractor/Twig/Gettext/Routing/Generator/UrlGenerator.php [moved from inc/3rdparty/Twig/Extensions/Gettext/Routing/Generator/UrlGenerator.php with 100% similarity]
vendor/umpirsky/twig-gettext-extractor/Twig/Gettext/Test/ExtractorTest.php [moved from inc/3rdparty/Twig/Extensions/Gettext/Test/ExtractorTest.php with 100% similarity]
vendor/umpirsky/twig-gettext-extractor/Twig/Gettext/Test/Fixtures/twig/empty.twig [moved from inc/3rdparty/Twig/Extensions/Gettext/Test/Fixtures/twig/empty.twig with 100% similarity]
vendor/umpirsky/twig-gettext-extractor/Twig/Gettext/Test/Fixtures/twig/plural.twig [moved from inc/3rdparty/Twig/Extensions/Gettext/Test/Fixtures/twig/plural.twig with 100% similarity]
vendor/umpirsky/twig-gettext-extractor/Twig/Gettext/Test/Fixtures/twig/singular.twig [moved from inc/3rdparty/Twig/Extensions/Gettext/Test/Fixtures/twig/singular.twig with 100% similarity]
vendor/umpirsky/twig-gettext-extractor/composer.json [new file with mode: 0644]
vendor/umpirsky/twig-gettext-extractor/phpunit.xml.dist [new file with mode: 0644]
vendor/umpirsky/twig-gettext-extractor/twig-gettext-extractor [new file with mode: 0755]

diff --git a/inc/3rdparty/Twig/Extensions/Autoloader.php b/inc/3rdparty/Twig/Extensions/Autoloader.php
deleted file mode 100644 (file)
index f23cced..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2009 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-/**
- * Autoloads Twig Extensions classes.
- *
- * @package    twig
- * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
- */
-class Twig_Extensions_Autoloader
-{
-    /**
-     * Registers Twig_Extensions_Autoloader as an SPL autoloader.
-     */
-    static public function register()
-    {
-        spl_autoload_register(array(new self, 'autoload'));
-    }
-
-    /**
-     * Handles autoloading of classes.
-     *
-     * @param  string  $class  A class name.
-     *
-     * @return boolean Returns true if the class has been loaded
-     */
-    static public function autoload($class)
-    {
-        if (0 !== strpos($class, 'Twig_Extensions')) {
-            return;
-        }
-
-        if (file_exists($file = dirname(__FILE__).'/../../'.str_replace('_', '/', $class).'.php')) {
-            require $file;
-        }
-    }
-}
diff --git a/inc/3rdparty/Twig/Extensions/Extension/Debug.php b/inc/3rdparty/Twig/Extensions/Extension/Debug.php
deleted file mode 100644 (file)
index 8974ce2..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2010 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-class Twig_Extensions_Extension_Debug extends Twig_Extension
-{
-    /**
-     * Returns the token parser instance to add to the existing list.
-     *
-     * @return array An array of Twig_TokenParser instances
-     */
-    public function getTokenParsers()
-    {
-        return array(
-            new Twig_Extensions_TokenParser_Debug(),
-        );
-    }
-
-    /**
-     * Returns the name of the extension.
-     *
-     * @return string The extension name
-     */
-    public function getName()
-    {
-        return 'debug';
-    }
-}
diff --git a/inc/3rdparty/Twig/Extensions/Extension/I18n.php b/inc/3rdparty/Twig/Extensions/Extension/I18n.php
deleted file mode 100644 (file)
index 3702aa2..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2010 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-class Twig_Extensions_Extension_I18n extends Twig_Extension
-{
-    /**
-     * Returns the token parser instances to add to the existing list.
-     *
-     * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
-     */
-    public function getTokenParsers()
-    {
-        return array(new Twig_Extensions_TokenParser_Trans());
-    }
-
-    /**
-     * Returns a list of filters to add to the existing list.
-     *
-     * @return array An array of filters
-     */
-    public function getFilters()
-    {
-        return array(
-            'trans' => new Twig_Filter_Function('gettext'),
-        );
-    }
-
-    /**
-     * Returns the name of the extension.
-     *
-     * @return string The extension name
-     */
-    public function getName()
-    {
-        return 'i18n';
-    }
-}
diff --git a/inc/3rdparty/Twig/Extensions/Extension/Intl.php b/inc/3rdparty/Twig/Extensions/Extension/Intl.php
deleted file mode 100644 (file)
index 40f7fc2..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2010 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-class Twig_Extensions_Extension_Intl extends Twig_Extension
-{
-    public function __construct()
-    {
-        if (!class_exists('IntlDateFormatter')) {
-            throw new RuntimeException('The intl extension is needed to use intl-based filters.');
-        }
-    }
-
-    /**
-     * Returns a list of filters to add to the existing list.
-     *
-     * @return array An array of filters
-     */
-    public function getFilters()
-    {
-        return array(
-            'localizeddate' => new Twig_Filter_Function('twig_localized_date_filter', array('needs_environment' => true)),
-        );
-    }
-
-    /**
-     * Returns the name of the extension.
-     *
-     * @return string The extension name
-     */
-    public function getName()
-    {
-        return 'intl';
-    }
-}
-
-function twig_localized_date_filter(Twig_Environment $env, $date, $dateFormat = 'medium', $timeFormat = 'medium', $locale = null, $timezone = null, $format = null)
-{
-    $date = twig_date_converter($env, $date, $timezone);
-
-    $formatValues = array(
-        'none'   => IntlDateFormatter::NONE,
-        'short'  => IntlDateFormatter::SHORT,
-        'medium' => IntlDateFormatter::MEDIUM,
-        'long'   => IntlDateFormatter::LONG,
-        'full'   => IntlDateFormatter::FULL,
-    );
-
-    $formatter = IntlDateFormatter::create(
-        $locale !== null ? $locale : Locale::getDefault(),
-        $formatValues[$dateFormat],
-        $formatValues[$timeFormat],
-        $date->getTimezone()->getName(),
-        IntlDateFormatter::GREGORIAN,
-        $format
-    );
-
-    return $formatter->format($date->getTimestamp());
-}
diff --git a/inc/3rdparty/Twig/Extensions/Extension/Text.php b/inc/3rdparty/Twig/Extensions/Extension/Text.php
deleted file mode 100644 (file)
index 0a3dc35..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-<?php
-
-/**
- * This file is part of Twig.
- *
- * (c) 2009 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- *
- * @author Henrik Bjornskov <hb@peytz.dk>
- * @package Twig
- * @subpackage Twig-extensions
- */
-class Twig_Extensions_Extension_Text extends Twig_Extension
-{
-    /**
-     * Returns a list of filters.
-     *
-     * @return array
-     */
-    public function getFilters()
-    {
-        $filters = array(
-            'truncate' => new Twig_Filter_Function('twig_truncate_filter', array('needs_environment' => true)),
-            'wordwrap' => new Twig_Filter_Function('twig_wordwrap_filter', array('needs_environment' => true)),
-        );
-
-        if (version_compare(Twig_Environment::VERSION, '1.5.0-DEV', '<')) {
-            $filters['nl2br'] = new Twig_Filter_Function('twig_nl2br_filter', array('pre_escape' => 'html', 'is_safe' => array('html')));
-        }
-
-        return $filters;
-    }
-
-    /**
-     * Name of this extension
-     *
-     * @return string
-     */
-    public function getName()
-    {
-        return 'Text';
-    }
-}
-
-function twig_nl2br_filter($value, $sep = '<br />')
-{
-    return str_replace("\n", $sep."\n", $value);
-}
-
-if (function_exists('mb_get_info')) {
-    function twig_truncate_filter(Twig_Environment $env, $value, $length = 30, $preserve = false, $separator = '...')
-    {
-        if (mb_strlen($value, $env->getCharset()) > $length) {
-            if ($preserve) {
-                if (false !== ($breakpoint = mb_strpos($value, ' ', $length, $env->getCharset()))) {
-                    $length = $breakpoint;
-                }
-            }
-
-            return rtrim(mb_substr($value, 0, $length, $env->getCharset())) . $separator;
-        }
-
-        return $value;
-    }
-
-    function twig_wordwrap_filter(Twig_Environment $env, $value, $length = 80, $separator = "\n", $preserve = false)
-    {
-        $sentences = array();
-
-        $previous = mb_regex_encoding();
-        mb_regex_encoding($env->getCharset());
-
-        $pieces = mb_split($separator, $value);
-        mb_regex_encoding($previous);
-
-        foreach ($pieces as $piece) {
-            while(!$preserve && mb_strlen($piece, $env->getCharset()) > $length) {
-                $sentences[] = mb_substr($piece, 0, $length, $env->getCharset());
-                $piece = mb_substr($piece, $length, 2048, $env->getCharset());
-            }
-
-            $sentences[] = $piece;
-        }
-
-        return implode($separator, $sentences);
-    }
-} else {
-    function twig_truncate_filter(Twig_Environment $env, $value, $length = 30, $preserve = false, $separator = '...')
-    {
-        if (strlen($value) > $length) {
-            if ($preserve) {
-                if (false !== ($breakpoint = strpos($value, ' ', $length))) {
-                    $length = $breakpoint;
-                }
-            }
-
-            return rtrim(substr($value, 0, $length)) . $separator;
-        }
-
-        return $value;
-    }
-
-    function twig_wordwrap_filter(Twig_Environment $env, $value, $length = 80, $separator = "\n", $preserve = false)
-    {
-        return wordwrap($value, $length, $separator, !$preserve);
-    }
-}
\ No newline at end of file
diff --git a/inc/3rdparty/Twig/Extensions/Grammar.php b/inc/3rdparty/Twig/Extensions/Grammar.php
deleted file mode 100644 (file)
index 4d031b1..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2010 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-abstract class Twig_Extensions_Grammar implements Twig_Extensions_GrammarInterface
-{
-    protected $name;
-    protected $parser;
-
-    public function __construct($name)
-    {
-        $this->name = $name;
-    }
-
-    public function setParser(Twig_ParserInterface $parser)
-    {
-        $this->parser = $parser;
-    }
-
-    public function getName()
-    {
-        return $this->name;
-    }
-}
diff --git a/inc/3rdparty/Twig/Extensions/Grammar/Arguments.php b/inc/3rdparty/Twig/Extensions/Grammar/Arguments.php
deleted file mode 100644 (file)
index 158c05a..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2010 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-class Twig_Extensions_Grammar_Arguments extends Twig_Extensions_Grammar
-{
-    public function __toString()
-    {
-        return sprintf('<%s:arguments>', $this->name);
-    }
-
-    public function parse(Twig_Token $token)
-    {
-        return $this->parser->getExpressionParser()->parseArguments();
-    }
-}
diff --git a/inc/3rdparty/Twig/Extensions/Grammar/Array.php b/inc/3rdparty/Twig/Extensions/Grammar/Array.php
deleted file mode 100644 (file)
index 34aece0..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2010 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-class Twig_Extensions_Grammar_Array extends Twig_Extensions_Grammar
-{
-    public function __toString()
-    {
-        return sprintf('<%s:array>', $this->name);
-    }
-
-    public function parse(Twig_Token $token)
-    {
-        return $this->parser->getExpressionParser()->parseArrayExpression();
-    }
-}
diff --git a/inc/3rdparty/Twig/Extensions/Grammar/Body.php b/inc/3rdparty/Twig/Extensions/Grammar/Body.php
deleted file mode 100644 (file)
index 540cfc7..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2010 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-class Twig_Extensions_Grammar_Body extends Twig_Extensions_Grammar
-{
-    protected $end;
-
-    public function __construct($name, $end = null)
-    {
-        parent::__construct($name);
-
-        $this->end = null === $end ? 'end'.$name : $end;
-    }
-
-    public function __toString()
-    {
-        return sprintf('<%s:body>', $this->name);
-    }
-
-    public function parse(Twig_Token $token)
-    {
-        $stream = $this->parser->getStream();
-        $stream->expect(Twig_Token::BLOCK_END_TYPE);
-
-        return $this->parser->subparse(array($this, 'decideBlockEnd'), true);
-    }
-
-    public function decideBlockEnd(Twig_Token $token)
-    {
-        return $token->test($this->end);
-    }
-}
diff --git a/inc/3rdparty/Twig/Extensions/Grammar/Boolean.php b/inc/3rdparty/Twig/Extensions/Grammar/Boolean.php
deleted file mode 100644 (file)
index c004809..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2010 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-class Twig_Extensions_Grammar_Boolean extends Twig_Extensions_Grammar
-{
-    public function __toString()
-    {
-        return sprintf('<%s:boolean>', $this->name);
-    }
-
-    public function parse(Twig_Token $token)
-    {
-        $this->parser->getStream()->expect(Twig_Token::NAME_TYPE, array('true', 'false'));
-
-        return new Twig_Node_Expression_Constant('true' === $token->getValue() ? true : false, $token->getLine());
-    }
-}
diff --git a/inc/3rdparty/Twig/Extensions/Grammar/Constant.php b/inc/3rdparty/Twig/Extensions/Grammar/Constant.php
deleted file mode 100644 (file)
index 9df6045..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2010 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-class Twig_Extensions_Grammar_Constant extends Twig_Extensions_Grammar
-{
-    protected $type;
-
-    public function __construct($name, $type = null)
-    {
-        $this->name = $name;
-        $this->type = null === $type ? Twig_Token::NAME_TYPE : $type;
-    }
-
-    public function __toString()
-    {
-        return $this->name;
-    }
-
-    public function parse(Twig_Token $token)
-    {
-        $this->parser->getStream()->expect($this->type, $this->name);
-
-        return $this->name;
-    }
-
-    public function getType()
-    {
-        return $this->type;
-    }
-}
diff --git a/inc/3rdparty/Twig/Extensions/Grammar/Expression.php b/inc/3rdparty/Twig/Extensions/Grammar/Expression.php
deleted file mode 100644 (file)
index 4c33df0..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2010 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-class Twig_Extensions_Grammar_Expression extends Twig_Extensions_Grammar
-{
-    public function __toString()
-    {
-        return sprintf('<%s>', $this->name);
-    }
-
-    public function parse(Twig_Token $token)
-    {
-        return $this->parser->getExpressionParser()->parseExpression();
-    }
-}
diff --git a/inc/3rdparty/Twig/Extensions/Grammar/Hash.php b/inc/3rdparty/Twig/Extensions/Grammar/Hash.php
deleted file mode 100644 (file)
index 98b07d2..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2010 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-class Twig_Extensions_Grammar_Hash extends Twig_Extensions_Grammar
-{
-    public function __toString()
-    {
-        return sprintf('<%s:hash>', $this->name);
-    }
-
-    public function parse(Twig_Token $token)
-    {
-        return $this->parser->getExpressionParser()->parseHashExpression();
-    }
-}
diff --git a/inc/3rdparty/Twig/Extensions/Grammar/Number.php b/inc/3rdparty/Twig/Extensions/Grammar/Number.php
deleted file mode 100644 (file)
index f0857d2..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2010 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-class Twig_Extensions_Grammar_Number extends Twig_Extensions_Grammar
-{
-    public function __toString()
-    {
-        return sprintf('<%s:number>', $this->name);
-    }
-
-    public function parse(Twig_Token $token)
-    {
-        $this->parser->getStream()->expect(Twig_Token::NUMBER_TYPE);
-
-        return new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
-    }
-}
diff --git a/inc/3rdparty/Twig/Extensions/Grammar/Optional.php b/inc/3rdparty/Twig/Extensions/Grammar/Optional.php
deleted file mode 100644 (file)
index da42748..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2010 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-class Twig_Extensions_Grammar_Optional extends Twig_Extensions_Grammar
-{
-    protected $grammar;
-
-    public function __construct()
-    {
-        $this->grammar = array();
-        foreach (func_get_args() as $grammar) {
-            $this->addGrammar($grammar);
-        }
-    }
-
-    public function __toString()
-    {
-        $repr = array();
-        foreach ($this->grammar as $grammar) {
-            $repr[] = (string) $grammar;
-        }
-
-        return sprintf('[%s]', implode(' ', $repr));
-    }
-
-    public function addGrammar(Twig_Extensions_GrammarInterface $grammar)
-    {
-        $this->grammar[] = $grammar;
-    }
-
-    public function parse(Twig_Token $token)
-    {
-        // test if we have the optional element before consuming it
-        if ($this->grammar[0] instanceof Twig_Extensions_Grammar_Constant) {
-            if (!$this->parser->getStream()->test($this->grammar[0]->getType(), $this->grammar[0]->getName())) {
-                return array();
-            }
-        } elseif ($this->grammar[0] instanceof Twig_Extensions_Grammar_Name) {
-            if (!$this->parser->getStream()->test(Twig_Token::NAME_TYPE)) {
-                return array();
-            }
-        } elseif ($this->parser->getStream()->test(Twig_Token::BLOCK_END_TYPE)) {
-            // if this is not a Constant or a Name, it must be the last element of the tag
-
-            return array();
-        }
-
-        $elements = array();
-        foreach ($this->grammar as $grammar) {
-            $grammar->setParser($this->parser);
-
-            $element = $grammar->parse($token);
-            if (is_array($element)) {
-                $elements = array_merge($elements, $element);
-            } else {
-                $elements[$grammar->getName()] = $element;
-            }
-        }
-
-        return $elements;
-    }
-}
diff --git a/inc/3rdparty/Twig/Extensions/Grammar/Switch.php b/inc/3rdparty/Twig/Extensions/Grammar/Switch.php
deleted file mode 100644 (file)
index 4245f2c..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2010 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-class Twig_Extensions_Grammar_Switch extends Twig_Extensions_Grammar
-{
-    public function __toString()
-    {
-        return sprintf('<%s:switch>', $this->name);
-    }
-
-    public function parse(Twig_Token $token)
-    {
-        $this->parser->getStream()->expect(Twig_Token::NAME_TYPE, $this->name);
-
-        return new Twig_Node_Expression_Constant(true, $token->getLine());
-    }
-}
diff --git a/inc/3rdparty/Twig/Extensions/Grammar/Tag.php b/inc/3rdparty/Twig/Extensions/Grammar/Tag.php
deleted file mode 100644 (file)
index 727f261..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2010 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-class Twig_Extensions_Grammar_Tag extends Twig_Extensions_Grammar
-{
-    protected $grammar;
-
-    public function __construct()
-    {
-        $this->grammar = array();
-        foreach (func_get_args() as $grammar) {
-            $this->addGrammar($grammar);
-        }
-    }
-
-    public function __toString()
-    {
-        $repr = array();
-        foreach ($this->grammar as $grammar) {
-            $repr[] = (string) $grammar;
-        }
-
-        return implode(' ', $repr);
-    }
-
-    public function addGrammar(Twig_Extensions_GrammarInterface $grammar)
-    {
-        $this->grammar[] = $grammar;
-    }
-
-    public function parse(Twig_Token $token)
-    {
-        $elements = array();
-        foreach ($this->grammar as $grammar) {
-            $grammar->setParser($this->parser);
-
-            $element = $grammar->parse($token);
-            if (is_array($element)) {
-                $elements = array_merge($elements, $element);
-            } else {
-                $elements[$grammar->getName()] = $element;
-            }
-        }
-
-        $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
-
-        return $elements;
-    }
-}
diff --git a/inc/3rdparty/Twig/Extensions/GrammarInterface.php b/inc/3rdparty/Twig/Extensions/GrammarInterface.php
deleted file mode 100644 (file)
index 22713bf..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2010 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-interface Twig_Extensions_GrammarInterface
-{
-    function setParser(Twig_ParserInterface $parser);
-
-    function parse(Twig_Token $token);
-
-    function getName();
-}
diff --git a/inc/3rdparty/Twig/Extensions/Node/Debug.php b/inc/3rdparty/Twig/Extensions/Node/Debug.php
deleted file mode 100644 (file)
index 7d01bbe..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2010 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-/**
- * Represents a debug node.
- *
- * @package    twig
- * @subpackage Twig-extensions
- * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
- * @version    SVN: $Id$
- */
-class Twig_Extensions_Node_Debug extends Twig_Node
-{
-    public function __construct(Twig_Node_Expression $expr = null, $lineno, $tag = null)
-    {
-        parent::__construct(array('expr' => $expr), array(), $lineno, $tag);
-    }
-
-    /**
-     * Compiles the node to PHP.
-     *
-     * @param Twig_Compiler A Twig_Compiler instance
-     */
-    public function compile(Twig_Compiler $compiler)
-    {
-        $compiler->addDebugInfo($this);
-
-        $compiler
-            ->write("if (\$this->env->isDebug()) {\n")
-            ->indent()
-        ;
-
-        if (null === $this->getNode('expr')) {
-            // remove embedded templates (macros) from the context
-            $compiler
-                ->write("\$vars = array();\n")
-                ->write("foreach (\$context as \$key => \$value) {\n")
-                ->indent()
-                ->write("if (!\$value instanceof Twig_Template) {\n")
-                ->indent()
-                ->write("\$vars[\$key] = \$value;\n")
-                ->outdent()
-                ->write("}\n")
-                ->outdent()
-                ->write("}\n")
-                ->write("var_dump(\$vars);\n")
-            ;
-        } else {
-            $compiler
-                ->write("var_dump(")
-                ->subcompile($this->getNode('expr'))
-                ->raw(");\n")
-            ;
-        }
-
-        $compiler
-            ->outdent()
-            ->write("}\n")
-        ;
-    }
-}
diff --git a/inc/3rdparty/Twig/Extensions/Node/Trans.php b/inc/3rdparty/Twig/Extensions/Node/Trans.php
deleted file mode 100644 (file)
index d12564a..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2010 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-/**
- * Represents a trans node.
- *
- * @package    twig
- * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
- */
-class Twig_Extensions_Node_Trans extends Twig_Node
-{
-    public function __construct(Twig_NodeInterface $body, Twig_NodeInterface $plural = null, Twig_Node_Expression $count = null, $lineno, $tag = null)
-    {
-        parent::__construct(array('count' => $count, 'body' => $body, 'plural' => $plural), array(), $lineno, $tag);
-    }
-
-    /**
-     * Compiles the node to PHP.
-     *
-     * @param Twig_Compiler A Twig_Compiler instance
-     */
-    public function compile(Twig_Compiler $compiler)
-    {
-        $compiler->addDebugInfo($this);
-
-        list($msg, $vars) = $this->compileString($this->getNode('body'));
-
-        if (null !== $this->getNode('plural')) {
-            list($msg1, $vars1) = $this->compileString($this->getNode('plural'));
-
-            $vars = array_merge($vars, $vars1);
-        }
-
-        $function = null === $this->getNode('plural') ? 'gettext' : 'ngettext';
-
-        if ($vars) {
-            $compiler
-                ->write('echo strtr('.$function.'(')
-                ->subcompile($msg)
-            ;
-
-            if (null !== $this->getNode('plural')) {
-                $compiler
-                    ->raw(', ')
-                    ->subcompile($msg1)
-                    ->raw(', abs(')
-                    ->subcompile($this->getNode('count'))
-                    ->raw(')')
-                ;
-            }
-
-            $compiler->raw('), array(');
-
-            foreach ($vars as $var) {
-                if ('count' === $var->getAttribute('name')) {
-                    $compiler
-                        ->string('%count%')
-                        ->raw(' => abs(')
-                        ->subcompile($this->getNode('count'))
-                        ->raw('), ')
-                    ;
-                } else {
-                    $compiler
-                        ->string('%'.$var->getAttribute('name').'%')
-                        ->raw(' => ')
-                        ->subcompile($var)
-                        ->raw(', ')
-                    ;
-                }
-            }
-
-            $compiler->raw("));\n");
-        } else {
-            $compiler
-                ->write('echo '.$function.'(')
-                ->subcompile($msg)
-            ;
-
-            if (null !== $this->getNode('plural')) {
-                $compiler
-                    ->raw(', ')
-                    ->subcompile($msg1)
-                    ->raw(', abs(')
-                    ->subcompile($this->getNode('count'))
-                    ->raw(')')
-                ;
-            }
-
-            $compiler->raw(");\n");
-        }
-    }
-
-    protected function compileString(Twig_NodeInterface $body)
-    {
-        if ($body instanceof Twig_Node_Expression_Name || $body instanceof Twig_Node_Expression_Constant || $body instanceof Twig_Node_Expression_TempName) {
-            return array($body, array());
-        }
-
-        $vars = array();
-        if (count($body)) {
-            $msg = '';
-
-            foreach ($body as $node) {
-                if (get_class($node) === 'Twig_Node' && $node->getNode(0) instanceof Twig_Node_SetTemp) {
-                    $node = $node->getNode(1);
-                }
-
-                if ($node instanceof Twig_Node_Print) {
-                    $n = $node->getNode('expr');
-                    while ($n instanceof Twig_Node_Expression_Filter) {
-                        $n = $n->getNode('node');
-                    }
-                    $msg .= sprintf('%%%s%%', $n->getAttribute('name'));
-                    $vars[] = new Twig_Node_Expression_Name($n->getAttribute('name'), $n->getLine());
-                } else {
-                    $msg .= $node->getAttribute('data');
-                }
-            }
-        } else {
-            $msg = $body->getAttribute('data');
-        }
-
-        return array(new Twig_Node(array(new Twig_Node_Expression_Constant(trim($msg), $body->getLine()))), $vars);
-    }
-}
diff --git a/inc/3rdparty/Twig/Extensions/SimpleTokenParser.php b/inc/3rdparty/Twig/Extensions/SimpleTokenParser.php
deleted file mode 100644 (file)
index 4954648..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2010 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-abstract class Twig_Extensions_SimpleTokenParser extends Twig_TokenParser
-{
-    /**
-     * Parses a token and returns a node.
-     *
-     * @param Twig_Token $token A Twig_Token instance
-     *
-     * @return Twig_NodeInterface A Twig_NodeInterface instance
-     */
-    public function parse(Twig_Token $token)
-    {
-        $grammar = $this->getGrammar();
-        if (!is_object($grammar)) {
-            $grammar = self::parseGrammar($grammar);
-        }
-
-        $grammar->setParser($this->parser);
-        $values = $grammar->parse($token);
-
-        return $this->getNode($values, $token->getLine());
-    }
-
-    /**
-     * Gets the grammar as an object or as a string.
-     *
-     * @return string|Twig_Extensions_Grammar A Twig_Extensions_Grammar instance or a string
-     */
-    abstract protected function getGrammar();
-
-    /**
-     * Gets the nodes based on the parsed values.
-     *
-     * @param array   $values An array of values
-     * @param integer $line   The parser line
-     */
-    abstract protected function getNode(array $values, $line);
-
-    protected function getAttribute($node, $attribute, $arguments = array(), $type = Twig_Node_Expression_GetAttr::TYPE_ANY, $line = -1)
-    {
-        return new Twig_Node_Expression_GetAttr(
-            $node instanceof Twig_NodeInterface ? $node : new Twig_Node_Expression_Name($node, $line),
-            $attribute instanceof Twig_NodeInterface ? $attribute : new Twig_Node_Expression_Constant($attribute, $line),
-            $arguments instanceof Twig_NodeInterface ? $arguments : new Twig_Node($arguments),
-            $type,
-            $line
-        );
-    }
-
-    protected function call($node, $attribute, $arguments = array(), $line = -1)
-    {
-        return $this->getAttribute($node, $attribute, $arguments, Twig_Node_Expression_GetAttr::TYPE_METHOD, $line);
-    }
-
-    protected function markAsSafe(Twig_NodeInterface $node, $line = -1)
-    {
-        return new Twig_Node_Expression_Filter(
-            $node,
-            new Twig_Node_Expression_Constant('raw', $line),
-            new Twig_Node(),
-            $line
-        );
-    }
-
-    protected function output(Twig_NodeInterface $node, $line = -1)
-    {
-        return new Twig_Node_Print($node, $line);
-    }
-
-    protected function getNodeValues(array $values)
-    {
-        $nodes = array();
-        foreach ($values as $value) {
-            if ($value instanceof Twig_NodeInterface) {
-                $nodes[] = $value;
-            }
-        }
-
-        return $nodes;
-    }
-
-    static public function parseGrammar($str, $main = true)
-    {
-        static $cursor;
-
-        if (true === $main) {
-            $cursor = 0;
-            $grammar = new Twig_Extensions_Grammar_Tag();
-        } else {
-            $grammar = new Twig_Extensions_Grammar_Optional();
-        }
-
-        while ($cursor < strlen($str)) {
-            if (preg_match('/\s+/A', $str, $match, null, $cursor)) {
-                $cursor += strlen($match[0]);
-            } elseif (preg_match('/<(\w+)(?:\:(\w+))?>/A', $str, $match, null, $cursor)) {
-                $class = sprintf('Twig_Extensions_Grammar_%s', ucfirst(isset($match[2]) ? $match[2] : 'Expression'));
-                if (!class_exists($class)) {
-                    throw new Twig_Error_Runtime(sprintf('Unable to understand "%s" in grammar (%s class does not exist)', $match[0], $class));
-                }
-                $grammar->addGrammar(new $class($match[1]));
-                $cursor += strlen($match[0]);
-            } elseif (preg_match('/\w+/A', $str, $match, null, $cursor)) {
-                $grammar->addGrammar(new Twig_Extensions_Grammar_Constant($match[0]));
-                $cursor += strlen($match[0]);
-            } elseif (preg_match('/,/A', $str, $match, null, $cursor)) {
-                $grammar->addGrammar(new Twig_Extensions_Grammar_Constant($match[0], Twig_Token::PUNCTUATION_TYPE));
-                $cursor += strlen($match[0]);
-            } elseif (preg_match('/\[/A', $str, $match, null, $cursor)) {
-                $cursor += strlen($match[0]);
-                $grammar->addGrammar(self::parseGrammar($str, false));
-            } elseif (true !== $main && preg_match('/\]/A', $str, $match, null, $cursor)) {
-                $cursor += strlen($match[0]);
-
-                return $grammar;
-            } else {
-                throw new Twig_Error_Runtime(sprintf('Unable to parse grammar "%s" near "...%s..."', $str, substr($str, $cursor, 10)));
-            }
-        }
-
-        return $grammar;
-    }
-}
diff --git a/inc/3rdparty/Twig/Extensions/TokenParser/Debug.php b/inc/3rdparty/Twig/Extensions/TokenParser/Debug.php
deleted file mode 100644 (file)
index 4a7dfcc..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2009-2010 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-class Twig_Extensions_TokenParser_Debug extends Twig_TokenParser
-{
-    /**
-     * Parses a token and returns a node.
-     *
-     * @param Twig_Token $token A Twig_Token instance
-     *
-     * @return Twig_NodeInterface A Twig_NodeInterface instance
-     */
-    public function parse(Twig_Token $token)
-    {
-        $lineno = $token->getLine();
-
-        $expr = null;
-        if (!$this->parser->getStream()->test(Twig_Token::BLOCK_END_TYPE)) {
-            $expr = $this->parser->getExpressionParser()->parseExpression();
-        }
-        $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
-
-        return new Twig_Extensions_Node_Debug($expr, $lineno, $this->getTag());
-    }
-
-    /**
-     * Gets the tag name associated with this token parser.
-     *
-     * @param string The tag name
-     */
-    public function getTag()
-    {
-        return 'debug';
-    }
-}
diff --git a/inc/3rdparty/Twig/Extensions/TokenParser/Trans.php b/inc/3rdparty/Twig/Extensions/TokenParser/Trans.php
deleted file mode 100644 (file)
index 5e2dc46..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2010 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-class Twig_Extensions_TokenParser_Trans extends Twig_TokenParser
-{
-    /**
-     * Parses a token and returns a node.
-     *
-     * @param Twig_Token $token A Twig_Token instance
-     *
-     * @return Twig_NodeInterface A Twig_NodeInterface instance
-     */
-    public function parse(Twig_Token $token)
-    {
-        $lineno = $token->getLine();
-        $stream = $this->parser->getStream();
-        $count = null;
-        $plural = null;
-
-        if (!$stream->test(Twig_Token::BLOCK_END_TYPE)) {
-            $body = $this->parser->getExpressionParser()->parseExpression();
-        } else {
-            $stream->expect(Twig_Token::BLOCK_END_TYPE);
-            $body = $this->parser->subparse(array($this, 'decideForFork'));
-            if ('plural' === $stream->next()->getValue()) {
-                $count = $this->parser->getExpressionParser()->parseExpression();
-                $stream->expect(Twig_Token::BLOCK_END_TYPE);
-                $plural = $this->parser->subparse(array($this, 'decideForEnd'), true);
-            }
-        }
-
-        $stream->expect(Twig_Token::BLOCK_END_TYPE);
-
-        $this->checkTransString($body, $lineno);
-
-        return new Twig_Extensions_Node_Trans($body, $plural, $count, $lineno, $this->getTag());
-    }
-
-    public function decideForFork(Twig_Token $token)
-    {
-        return $token->test(array('plural', 'endtrans'));
-    }
-
-    public function decideForEnd(Twig_Token $token)
-    {
-        return $token->test('endtrans');
-    }
-
-    /**
-     * Gets the tag name associated with this token parser.
-     *
-     * @param string The tag name
-     */
-    public function getTag()
-    {
-        return 'trans';
-    }
-
-    protected function checkTransString(Twig_NodeInterface $body, $lineno)
-    {
-        foreach ($body as $i => $node) {
-            if (
-                $node instanceof Twig_Node_Text
-                ||
-                ($node instanceof Twig_Node_Print && $node->getNode('expr') instanceof Twig_Node_Expression_Name)
-            ) {
-                continue;
-            }
-
-            throw new Twig_Error_Syntax(sprintf('The text to be translated with "trans" can only contain references to simple variables'), $lineno);
-        }
-    }
-}
diff --git a/inc/3rdparty/Twig/Gettext/Extractor.php b/inc/3rdparty/Twig/Gettext/Extractor.php
deleted file mode 100644 (file)
index e7fa1af..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-<?php
-
-/**
- * This file is part of the Twig Gettext utility.
- *
- *  (c) Саша Стаменковић <umpirsky@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Twig\Gettext;
-
-use Symfony\Component\Filesystem\Filesystem;
-
-/**
- * Extracts translations from twig templates.
- *
- * @author Саша Стаменковић <umpirsky@gmail.com>
- */
-class Extractor
-{
-    /**
-     * @var \Twig_Environment
-     */
-    protected $environment;
-
-    /**
-     * Template cached file names.
-     *
-     * @var string[]
-     */
-    protected $templates;
-
-    /**
-     * Gettext parameters.
-     *
-     * @var string[]
-     */
-    protected $parameters;
-
-    public function __construct(\Twig_Environment $environment)
-    {
-        $this->environment = $environment;
-        $this->reset();
-    }
-
-    protected function reset()
-    {
-        $this->templates = array();
-        $this->parameters = array();
-    }
-
-    public function addTemplate($path)
-    {
-        $this->environment->loadTemplate($path);
-        $this->templates[] = $this->environment->getCacheFilename($path);
-    }
-
-    public function addGettextParameter($parameter)
-    {
-        $this->parameters[] = $parameter;
-    }
-
-    public function setGettextParameters(array $parameters)
-    {
-        $this->parameters = $parameters;
-    }
-
-    public function extract()
-    {
-        $command = 'xgettext';
-        $command .= ' '.join(' ', $this->parameters);
-        $command .= ' '.join(' ', $this->templates);
-
-        $error = 0;
-        $output = system($command, $error);
-        if (0 !== $error) {
-            throw new \RuntimeException(sprintf(
-                'Gettext command "%s" failed with error code %s and output: %s',
-                $command,
-                $error,
-                $output
-            ));
-        }
-
-        $this->reset();
-    }
-
-    public function __destruct()
-    {
-        $filesystem = new Filesystem();
-        $filesystem->remove($this->environment->getCache());
-    }
-}
diff --git a/inc/3rdparty/Twig/Gettext/Loader/Filesystem.php b/inc/3rdparty/Twig/Gettext/Loader/Filesystem.php
deleted file mode 100644 (file)
index b011b03..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-
-/**
- * This file is part of the Twig Gettext utility.
- *
- *  (c) Саша Стаменковић <umpirsky@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Twig\Gettext\Loader;
-
-/**
- * Loads template from the filesystem.
- *
- * @author Саша Стаменковић <umpirsky@gmail.com>
- */
-class Filesystem extends \Twig_Loader_Filesystem
-{
-    /**
-     * Hacked find template to allow loading templates by absolute path.
-     *
-     * @param string $name template name or absolute path
-     */
-    protected function findTemplate($name)
-    {
-        // normalize name
-        $name = preg_replace('#/{2,}#', '/', strtr($name, '\\', '/'));
-
-        if (isset($this->cache[$name])) {
-            return $this->cache[$name];
-        }
-
-        $this->validateName($name);
-
-        $namespace = '__main__';
-        if (isset($name[0]) && '@' == $name[0]) {
-            if (false === $pos = strpos($name, '/')) {
-                throw new \InvalidArgumentException(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name));
-            }
-
-            $namespace = substr($name, 1, $pos - 1);
-
-            $name = substr($name, $pos + 1);
-        }
-
-        if (!isset($this->paths[$namespace])) {
-            throw new \Twig_Error_Loader(sprintf('There are no registered paths for namespace "%s".', $namespace));
-        }
-
-        if (is_file($name)) {
-            return $this->cache[$name] = $name;
-        }
-
-        return __DIR__.'/../Test/Fixtures/twig/empty.twig';
-    }
-}
diff --git a/inc/3rdparty/Twig/Gettext/Routing/Generator/UrlGenerator.php b/inc/3rdparty/Twig/Gettext/Routing/Generator/UrlGenerator.php
deleted file mode 100644 (file)
index 9e3431b..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-/**
- * This file is part of the Twig Gettext utility.
- *
- *  (c) Саша Стаменковић <umpirsky@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Twig\Gettext\Routing\Generator;
-
-use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
-use Symfony\Component\Routing\RequestContext;
-
-/**
- * Dummy url generator.
- *
- * @author Саша Стаменковић <umpirsky@gmail.com>
- */
-class UrlGenerator implements UrlGeneratorInterface
-{
-    protected $context;
-
-    public function generate($name, $parameters = array(), $absolute = false)
-    {
-    }
-
-    public function getContext()
-    {
-        return $this->context;
-    }
-
-    public function setContext(RequestContext $context)
-    {
-        $this->context = $context;
-    }
-}
diff --git a/inc/3rdparty/Twig/Gettext/Test/ExtractorTest.php b/inc/3rdparty/Twig/Gettext/Test/ExtractorTest.php
deleted file mode 100644 (file)
index d467835..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-<?php
-
-/**
- * This file is part of the Twig Gettext utility.
- *
- *  (c) Саша Стаменковић <umpirsky@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Twig\Gettext\Test;
-
-use Twig\Gettext\Extractor;
-use Twig\Gettext\Loader\Filesystem;
-use Symfony\Component\Translation\Loader\PoFileLoader;
-
-/**
- * @author Саша Стаменковић <umpirsky@gmail.com>
- */
-class ExtractorTest extends \PHPUnit_Framework_TestCase
-{
-    /**
-     * @var \Twig_Environment
-     */
-    protected $twig;
-
-    /**
-     * @var PoFileLoader
-     */
-    protected $loader;
-
-    protected function setUp()
-    {
-        $this->twig = new \Twig_Environment(new Filesystem('/'), array(
-            'cache'       => '/tmp/cache/'.uniqid(),
-            'auto_reload' => true
-        ));
-        $this->twig->addExtension(new \Twig_Extensions_Extension_I18n());
-
-        $this->loader = new PoFileLoader();
-    }
-
-    /**
-     * @dataProvider testExtractDataProvider
-     */
-    public function testExtract(array $templates, array $parameters, array $messages)
-    {
-        $extractor = new Extractor($this->twig);
-
-        foreach ($templates as $template) {
-            $extractor->addTemplate($template);
-        }
-        foreach ($parameters as $parameter) {
-            $extractor->addGettextParameter($parameter);
-        }
-
-        $extractor->extract();
-
-        $catalog = $this->loader->load($this->getPotFile(), null);
-
-        foreach ($messages as $message) {
-            $this->assertTrue(
-                $catalog->has($message),
-                sprintf('Message "%s" not found in catalog.', $message)
-            );
-        }
-    }
-
-    public function testExtractDataProvider()
-    {
-        return array(
-            array(
-                array(
-                    __DIR__.'/Fixtures/twig/singular.twig',
-                    __DIR__.'/Fixtures/twig/plural.twig',
-                ),
-                $this->getGettextParameters(),
-                array(
-                    'Hello %name%!',
-                    'Hello World!',
-                    'Hey %name%, I have one apple.',
-                    'Hey %name%, I have %count% apples.',
-                ),
-            ),
-        );
-    }
-
-    public function testExtractNoTranslations()
-    {
-        $extractor = new Extractor($this->twig);
-
-        $extractor->addTemplate(__DIR__.'/Fixtures/twig/empty.twig');
-        $extractor->setGettextParameters($this->getGettextParameters());
-
-        $extractor->extract();
-
-        $catalog = $this->loader->load($this->getPotFile(), null);
-
-        $this->assertEmpty($catalog->all('messages'));
-    }
-
-    private function getPotFile()
-    {
-        return __DIR__.'/Fixtures/messages.pot';
-    }
-
-    private function getGettextParameters()
-    {
-        return array(
-            '--force-po',
-            '-o',
-            $this->getPotFile(),
-        );
-    }
-
-    protected function tearDown()
-    {
-        if (file_exists($this->getPotFile())) {
-            unlink($this->getPotFile());
-        }
-    }
-}
diff --git a/inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/empty.twig b/inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/empty.twig
deleted file mode 100644 (file)
index 05f0d26..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Nothing to translate here.
diff --git a/inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/plural.twig b/inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/plural.twig
deleted file mode 100644 (file)
index f9754ff..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-{% trans %}
-    Hey {{ name }}, I have one apple.
-{% plural apple_count %}
-    Hey {{ name }}, I have {{ count }} apples.
-{% endtrans %}
diff --git a/inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/singular.twig b/inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/singular.twig
deleted file mode 100644 (file)
index d757cf9..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-{% trans "Hello World!" %}
-
-{% trans %}
-    Hello World!
-{% endtrans %}
-
-{% trans %}
-    Hello {{ name }}!
-{% endtrans %}
index b78147aba13a46b9d789e89469f3a51cbea11f1c..495dbb85548b013c816092f708beee883e739d11 100644 (file)
@@ -24,15 +24,14 @@ define ('LANG', 'fr_FR.UTF8');
 $storage_type = 'sqlite'; # sqlite, file
 
 # /!\ Be careful if you change the lines below /!\
-
 require_once 'poche/pocheTools.class.php';
 require_once 'poche/pocheCore.php';
 require_once '3rdparty/Readability.php';
 require_once '3rdparty/Encoding.php';
 require_once '3rdparty/Session.class.php';
-require_once '3rdparty/Twig/Autoloader.php';
 require_once 'store/store.class.php';
 require_once 'store/' . $storage_type . '.class.php';
+require_once './vendor/autoload.php';
 
 if (DOWNLOAD_PICTURES) {
     require_once 'poche/pochePicture.php';
@@ -45,7 +44,7 @@ bindtextdomain(LANG, LOCALE);
 textdomain(LANG); 
 
 # template engine
-Twig_Autoloader::register();
+// Twig_Autoloader::register();
 $loader = new Twig_Loader_Filesystem(TPL);
 $twig = new Twig_Environment($loader, array(
     'cache' => CACHE,
index 81bd017449be7a7f7c260288eb93a459c269d649..dc064428d3a6a7f620bb210c24771c848a395be6 100644 (file)
--- a/index.php
+++ b/index.php
 
 include dirname(__FILE__).'/inc/config.php';
 
-$errors = array();
+$notices = array();
 
 # XSRF protection with token
-if (!empty($_POST)) {
-    if (!Session::isToken($_POST['token'])) {
-        #die(_('Wrong token'));
-        // TODO CORRIGER ICI !!! 
-    }
-    unset($_SESSION['tokens']);
-}
+// if (!empty($_POST)) {
+//     if (!Session::isToken($_POST['token'])) {
+//         die(_('Wrong token'));
+//         // TODO remettre le test
+//     }
+//     unset($_SESSION['tokens']);
+// }
 
 $referer = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER'];
 
 if (isset($_GET['login'])) {
+    # hello you
     if (!empty($_POST['login']) && !empty($_POST['password'])) {
         if (Session::login($_SESSION['login'], $_SESSION['pass'], $_POST['login'], encode_string($_POST['password'] . $_POST['login']))) {
             pocheTools::logm('login successful');
-            $errors[]['value'] = _('login successful');
+            $pocheTools[]['value'] = _('login successful');
 
             if (!empty($_POST['longlastingsession'])) {
                 $_SESSION['longlastingsession'] = 31536000;
                 $_SESSION['expires_on'] = time() + $_SESSION['longlastingsession'];
                 session_set_cookie_params($_SESSION['longlastingsession']);
             } else {
-                session_set_cookie_params(0); // when browser closes
+                session_set_cookie_params(0);
             }
             session_regenerate_id(true);
             pocheTools::redirect($referer);
         }
         pocheTools::logm('login failed');
-        $errors[]['value'] = _('Login failed !');
+        $notices[]['value'] = _('Login failed !');
+        pocheTools::redirect();
     } else {
         pocheTools::logm('login failed');
+        pocheTools::redirect();
     }
 }
 elseif (isset($_GET['logout'])) {
+    # see you soon !
     pocheTools::logm('logout');
     Session::logout();
     pocheTools::redirect();
 }
 elseif  (isset($_GET['config'])) {
+    # Update password
     if (isset($_POST['password']) && isset($_POST['password_repeat'])) {
         if ($_POST['password'] == $_POST['password_repeat'] && $_POST['password'] != "") {
-            pocheTools::logm('password updated');
             if (!MODE_DEMO) {
+                pocheTools::logm('password updated');
                 $store->updatePassword(encode_string($_POST['password'] . $_SESSION['login']));
-                #your password has been updated
+                Session::logout();
+                pocheTools::redirect();
             }
             else {
-                #in demo mode, you can\'t update password
+                pocheTools::logm('in demo mode, you can\'t do this');
             }
         }
-        #else
-        #your password can\'t be empty and you have to repeat it in the second field
     }
 }
 
-# Traitement des paramètres et déclenchement des actions
-$view               = (isset ($_REQUEST['view'])) ? htmlentities($_REQUEST['view']) : 'home';
-$full_head          = (isset ($_REQUEST['full_head'])) ? htmlentities($_REQUEST['full_head']) : 'yes';
-$action             = (isset ($_REQUEST['action'])) ? htmlentities($_REQUEST['action']) : '';
-$_SESSION['sort']   = (isset ($_REQUEST['sort'])) ? htmlentities($_REQUEST['sort']) : 'id';
-$id                 = (isset ($_REQUEST['id'])) ? htmlspecialchars($_REQUEST['id']) : '';
-$url                = (isset ($_GET['url'])) ? $_GET['url'] : '';
+# Aaaaaaand action !
+$view = (isset ($_REQUEST['view'])) ? htmlentities($_REQUEST['view']) : 'home';
+$full_head = (isset ($_REQUEST['full_head'])) ? htmlentities($_REQUEST['full_head']) : 'yes';
+$action = (isset ($_REQUEST['action'])) ? htmlentities($_REQUEST['action']) : '';
+$_SESSION['sort'] = (isset ($_REQUEST['sort'])) ? htmlentities($_REQUEST['sort']) : 'id';
+$id = (isset ($_REQUEST['id'])) ? htmlspecialchars($_REQUEST['id']) : '';
+$url = (isset ($_GET['url'])) ? $_GET['url'] : '';
 
 $tpl_vars = array(
     'referer' => $referer,
@@ -82,7 +86,7 @@ $tpl_vars = array(
     'demo' => MODE_DEMO,
     'title' => _('poche, a read it later open source system'),
     'token' => Session::getToken(),
-    'errors' => $errors,
+    'notices' => $notices,
 );
 
 if (Session::isLogged()) {
index 2d640cbf69353a2739067c1d72d272300a76edef..ad96e9d1e9a2985f7e218f9dd87c7ea195b796af 100644 (file)
@@ -1,10 +1,10 @@
-        <link rel="shortcut icon" type="image/x-icon" href="./img/favicon.ico" />
-        <link rel="apple-touch-icon-precomposed" sizes="144x144" href="./img/apple-touch-icon-144x144-precomposed.png">
-        <link rel="apple-touch-icon-precomposed" sizes="72x72" href="./img/apple-touch-icon-72x72-precomposed.png">
-        <link rel="apple-touch-icon-precomposed" href="./img/apple-touch-icon-precomposed.png">
-        <link rel="stylesheet" href="./css/knacss.css" media="all">
-        <link rel="stylesheet" href="./css/style.css" media="all">
+        <link rel="shortcut icon" type="image/x-icon" href="./tpl/img/favicon.ico" />
+        <link rel="apple-touch-icon-precomposed" sizes="144x144" href="./tpl/img/apple-touch-icon-144x144-precomposed.png">
+        <link rel="apple-touch-icon-precomposed" sizes="72x72" href="./tpl/img/apple-touch-icon-72x72-precomposed.png">
+        <link rel="apple-touch-icon-precomposed" href="./tpl/img/apple-touch-icon-precomposed.png">
+        <link rel="stylesheet" href="./tpl/css/knacss.css" media="all">
+        <link rel="stylesheet" href="./tpl/css/style.css" media="all">
         <!-- Light Theme -->
-        <link rel="stylesheet" href="./css/style-light.css" media="all" title="light-style">
+        <link rel="stylesheet" href="./tpl/css/style-light.css" media="all" title="light-style">
         <!-- Dark Theme -->
-        <link rel="alternate stylesheet" href="./css/style-dark.css" media="all" title="dark-style">
\ No newline at end of file
+        <link rel="alternate stylesheet" href="./tpl/css/style-dark.css" media="all" title="dark-style">
\ No newline at end of file
index 8e3ea7e08f770dbaa20fc78d9eeeb9bf9e4a6827..daee44fc8e90e5a99d72ef84c53ef3173a6df255 100644 (file)
@@ -1,3 +1,3 @@
         <header>
-            <h1><a href="./"><img src="./img/logo.png" alt="logo poche" /></a>poche</h1>
+            <h1><a href="./"><img src="./tpl/img/logo.png" alt="logo poche" /></a>poche</h1>
         </header>
\ No newline at end of file
index 10c481deab17473f2923129bedc1f66602fc6861..be2c04547e24f7b02148b9282313771c36ae31e1 100644 (file)
 {% endblock %}
 {% block content %}
             <div id="content">
-               <h2>Bookmarklet</h2>
-               <p>Thanks to the bookmarklet, you will be able to easily add a link to your poche. If you don't know how use a bookmarklet, <a href="http://support.mozilla.org/en-US/kb/bookmarklets-perform-common-web-page-tasks">have a look here</a>.</p>
-               <p>Drag & drop this link to your bookmarks bar and have fun with poche.</p>
-                <p><a ondragend="this.click();" style="cursor: move; border: 1px dashed grey; background: white;" title="i am a bookmarklet, use me !" href="javascript:if(top['bookmarklet-url@inthepoche.com']){top['bookmarklet-url@inthepoche.com'];}else{(function(){var%20url%20=%20location.href%20||%20url;window.open('{$poche_url}?action=add&url='%20+%20btoa(url),'_self');})();void(0);}">poche it !</a></p>
+               <h2>{% trans "Bookmarklet" %}</h2>
+               <p>{% trans "Thanks to the bookmarklet, you will be able to easily add a link to your poche." %} {% trans "Have a look to this documentation:" %} <a href="http://inthepoche.com/?pages/Documentation" target="_blank">http://inthepoche.com/?pages/Documentation</a>.</p>
+               <p>{% trans "Drag & drop this link to your bookmarks bar and have fun with poche." %}</p>
+                <p><a ondragend="this.click();" style="cursor: move; border: 1px dashed grey; background: white;" title="i am a bookmarklet, use me !" href="javascript:if(top['bookmarklet-url@inthepoche.com']){top['bookmarklet-url@inthepoche.com'];}else{(function(){var%20url%20=%20location.href%20||%20url;window.open('{$poche_url}?action=add&url='%20+%20btoa(url),'_self');})();void(0);}">{% trans "poche it!" %}</a></p>
 
-                <h2>Password</h2>
+                <h2>{% trans "Change your password" %}</h2>
                    <form method="post" action="?config" name="loginform">
                        <fieldset class="w500p">
                            <div class="row">
-                               <label class="col w150p" for="password">New password</label>
-                               <input class="col" type="password" id="password" name="password" placeholder="Password" tabindex="2">
+                               <label class="col w150p" for="password">{% trans "New password" %}</label>
+                               <input class="col" type="password" id="password" name="password" placeholder="{% trans "Password" %}" tabindex="2">
                            </div>
                            <div class="row">
-                               <label class="col w150p" for="password_repeat">Repeat your new password</label>
-                               <input class="col" type="password" id="password_repeat" name="password_repeat" placeholder="Password" tabindex="3">
+                               <label class="col w150p" for="password_repeat">{% trans "Repeat your new password" %}</label>
+                               <input class="col" type="password" id="password_repeat" name="password_repeat" placeholder="{% trans "Password" %}" tabindex="3">
                            </div>
                            <div class="row mts txtcenter">
-                               <button class="bouton" type="submit" tabindex="4">Update</button>
+                               <button class="bouton" type="submit" tabindex="4">{% trans "Update" %}</button>
                            </div>
                        </fieldset>
-                       <input type="hidden" name="returnurl" value="<?php echo htmlspecialchars($referer);?>">
-                       <input type="hidden" name="token" value="<?php echo Session::getToken(); ?>">
+                       <input type="hidden" name="returnurl" value="{{ referer }}">
+                       <input type="hidden" name="token" value="{{ token }}">
                    </form>
-                <h2>Export</h2>
-                <p><a href="?view=export" target="_blank">Click here</a> to export your poche datas.</p>
+                <h2>{% trans "Export your poche datas" %}</h2>
+                <p><a href="?view=export" target="_blank">{% trans "Click here" %}</a> {% trans "to export your poche datas." %}</p>
             </div>
 {% endblock %}
\ No newline at end of file
similarity index 100%
rename from css/knacss.css
rename to tpl/css/knacss.css
similarity index 100%
rename from css/style-dark.css
rename to tpl/css/style-dark.css
similarity index 100%
rename from css/style-light.css
rename to tpl/css/style-light.css
similarity index 100%
rename from css/style.css
rename to tpl/css/style.css
diff --git a/tpl/entries.html b/tpl/entries.html
deleted file mode 100644 (file)
index 83e58c7..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-            <div id="content">
-                {loop="entries"}
-                    <div id="entry-{$value.id}" class="entrie mb2">
-                        <span class="content">
-                            <h2 class="h6-like">
-                                <a href="index.php?&view=view&id={$value.id}">{$value.title}</a>
-                            </h2>
-                            <div class="tools">
-                                <ul>
-                                    <li>
-                                        <a title="toggle mark as read" class="tool archive {if="$value.is_read == '0'"}archive-off{/if}" onclick="toggle_archive(this, {$value.id})"><span></span></a></li>
-                                        <li><a title="toggle favorite" class="tool fav {if="$value.is_fav == '0'"}fav-off{/if}" onclick="toggle_favorite(this, {$value.id})"><span></span></a></li>
-                                        <li><form method="post" onsubmit="return confirm('Are you sure?')" style="display: inline;"><input type="hidden" name="token" id="token" value="<?php echo Session::getToken(); ?>" /><input type="hidden" id="action" name="action" value="delete" /><input type="hidden" id="view" name="view" value="{$view}" /><input type="hidden" id="id" name="id" value="{$value.id}" /><input type="submit" class="delete" title="toggle delete" /></form>
-                                    </li>
-                                </ul>
-                            </div>
-                            <div class="url">{$value.url}</div>
-                        </span>
-                    </div>
-                {/loop}
-            </div>
\ No newline at end of file
index c79d4276781953ac558273577c33f4abefffadeb..5752b2cf2bc4ba8ef9e6ebaf8859a6aef798c9ed 100644 (file)
 {% endblock %}
 {% block precontent %}
             <ul id="sort">
-                <li><img src="img/up.png" onclick="sort_links('{{ view }}', 'ia');" title="{% trans "by date asc" %}" /> {% trans "by date" %} <img src="img/down.png" onclick="sort_links('{{ view }}', 'id');" title="{% trans "by date desc" %}" /></li>
-                <li><img src="img/up.png" onclick="sort_links('{{ view }}', 'ta');" title="{% trans "by title asc" %}" /> {% trans "by title" %} <img src="img/down.png" onclick="sort_links('{{ view }}', 'td');" title="{% trans "by title desc" %}" /></li>
+                <li><img src="./tpl/img/up.png" onclick="sort_links('{{ view }}', 'ia');" title="{% trans "by date asc" %}" /> {% trans "by date" %} <img src="./tpl/img/down.png" onclick="sort_links('{{ view }}', 'id');" title="{% trans "by date desc" %}" /></li>
+                <li><img src="./tpl/img/up.png" onclick="sort_links('{{ view }}', 'ta');" title="{% trans "by title asc" %}" /> {% trans "by title" %} <img src="./tpl/img/down.png" onclick="sort_links('{{ view }}', 'td');" title="{% trans "by title desc" %}" /></li>
             </ul>
 {% endblock %}
+{% block notices %}
+            <div class="messages">
+                <ul>
+                {% for notice in notices %}
+                    <li>{{ notice.value|e }}</li>
+                {% endfor %}
+                </ul>
+            </div>
+{% endblock %}
 {% block content %}
             <div id="content">
                 {% for entry in entries %}
@@ -28,7 +37,7 @@
                                     <li>
                                         <a title="{% trans "toggle mark as read" %}" class="tool archive {% if entry.is_read == 0 %}archive-off{% endif %}" onclick="toggle_archive(this, {{ entry.id|e }})"><span></span></a></li>
                                         <li><a title="{% trans "toggle favorite" %}" class="tool fav {% if entry.is_fav == 0 %}fav-off{% endif %}" onclick="toggle_favorite(this, {{ entry.id|e }})"><span></span></a></li>
-                                        <li><form method="post" onsubmit="return confirm('{% trans "are you sure?" %}')" style="display: inline;"><input type="hidden" name="token" id="token" value="{{ token }}" /><input type="hidden" id="action" name="action" value="delete" /><input type="hidden" id="view" name="view" value="{{ view }}" /><input type="hidden" id="id" name="id" value="{{ entry.id|e }}" /><input type="submit" class="delete" title="{% trans "toggle delete" %}" /></form>
+                                        <li><form method="post" style="display: inline;"><input type="hidden" name="token" id="token" value="{{ token }}" /><input type="hidden" id="action" name="action" value="delete" /><input type="hidden" id="view" name="view" value="{{ view }}" /><input type="hidden" id="id" name="id" value="{{ entry.id|e }}" /><input type="submit" class="delete" title="{% trans "toggle delete" %}" /></form>
                                     </li>
                                 </ul>
                             </div>
@@ -40,9 +49,9 @@
 {% endblock %}
 
 {% block js %}
-            <script type="text/javascript" src="js/jquery-1.9.1.min.js"></script>
-            <script type="text/javascript" src="js/poche.js"></script>
-            <script type="text/javascript" src="js/jquery.masonry.min.js"></script>
+            <script type="text/javascript" src="./tpl/js/jquery-1.9.1.min.js"></script>
+            <script type="text/javascript" src="./tpl/js/poche.js"></script>
+            <script type="text/javascript" src="./tpl/js/jquery.masonry.min.js"></script>
             <script type="text/javascript">
                 $( window ).load( function()
                 {
similarity index 100%
rename from img/dark/down.png
rename to tpl/img/dark/down.png
similarity index 100%
rename from img/dark/logo.png
rename to tpl/img/dark/logo.png
similarity index 100%
rename from img/dark/remove.png
rename to tpl/img/dark/remove.png
similarity index 100%
rename from img/dark/up.png
rename to tpl/img/dark/up.png
similarity index 100%
rename from img/down.png
rename to tpl/img/down.png
similarity index 100%
rename from img/favicon.ico
rename to tpl/img/favicon.ico
similarity index 100%
rename from img/logo.png
rename to tpl/img/logo.png
similarity index 100%
rename from img/up.png
rename to tpl/img/up.png
similarity index 100%
rename from js/poche.js
rename to tpl/js/poche.js
index d10805301f92ba93d179b956cebd500c80651ea1..fcb9b4d63ef6cf39fbc6845648bd1190d703dfa6 100644 (file)
@@ -1,11 +1,11 @@
 {% extends "layout.twig" %}
 
 {% block title %}{% trans "login to your poche" %}{% endblock %}
-{% block messages %}
+{% block notices %}
             <div class="messages">
                 <ul>
-                {% for error in errors %}
-                    <li>{{ error.value|e }}</li>
+                {% for notice in notices %}
+                    <li>{{ notice.value|e }}</li>
                 {% endfor %}
                 </ul>
             </div>
@@ -28,7 +28,7 @@
                                                <label class="col w150p">{% trans "Stay signed in" %}</label>
                                                <div class="col">
                                                        <input type="checkbox" name="longlastingsession" tabindex="3">
-                                                       <small class="inbl">(Do not check on public computers)</small>
+                                                       <small class="inbl">{% trans "(Do not check on public computers)" %}</small>
                                                </div>
                                        </div>
                                        <div class="row mts txtcenter">
diff --git a/vendor/autoload.php b/vendor/autoload.php
new file mode 100644 (file)
index 0000000..9e19b46
--- /dev/null
@@ -0,0 +1,7 @@
+<?php
+
+// autoload.php generated by Composer
+
+require_once __DIR__ . '/composer' . '/autoload_real.php';
+
+return ComposerAutoloaderInit1c7743925d207055d2ad189b1f10a029::getLoader();
diff --git a/vendor/bin/twig-gettext-extractor b/vendor/bin/twig-gettext-extractor
new file mode 120000 (symlink)
index 0000000..006816c
--- /dev/null
@@ -0,0 +1 @@
+../umpirsky/twig-gettext-extractor/twig-gettext-extractor
\ No newline at end of file
diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php
new file mode 100644 (file)
index 0000000..1db8d9a
--- /dev/null
@@ -0,0 +1,246 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0 class loader
+ *
+ * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
+ *
+ *     $loader = new \Composer\Autoload\ClassLoader();
+ *
+ *     // register classes with namespaces
+ *     $loader->add('Symfony\Component', __DIR__.'/component');
+ *     $loader->add('Symfony',           __DIR__.'/framework');
+ *
+ *     // activate the autoloader
+ *     $loader->register();
+ *
+ *     // to enable searching the include path (eg. for PEAR packages)
+ *     $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class ClassLoader
+{
+    private $prefixes = array();
+    private $fallbackDirs = array();
+    private $useIncludePath = false;
+    private $classMap = array();
+
+    public function getPrefixes()
+    {
+        return call_user_func_array('array_merge', $this->prefixes);
+    }
+
+    public function getFallbackDirs()
+    {
+        return $this->fallbackDirs;
+    }
+
+    public function getClassMap()
+    {
+        return $this->classMap;
+    }
+
+    /**
+     * @param array $classMap Class to filename map
+     */
+    public function addClassMap(array $classMap)
+    {
+        if ($this->classMap) {
+            $this->classMap = array_merge($this->classMap, $classMap);
+        } else {
+            $this->classMap = $classMap;
+        }
+    }
+
+    /**
+     * Registers a set of classes, merging with any others previously set.
+     *
+     * @param string       $prefix  The classes prefix
+     * @param array|string $paths   The location(s) of the classes
+     * @param bool         $prepend Prepend the location(s)
+     */
+    public function add($prefix, $paths, $prepend = false)
+    {
+        if (!$prefix) {
+            if ($prepend) {
+                $this->fallbackDirs = array_merge(
+                    (array) $paths,
+                    $this->fallbackDirs
+                );
+            } else {
+                $this->fallbackDirs = array_merge(
+                    $this->fallbackDirs,
+                    (array) $paths
+                );
+            }
+
+            return;
+        }
+
+        $first = $prefix[0];
+        if (!isset($this->prefixes[$first][$prefix])) {
+            $this->prefixes[$first][$prefix] = (array) $paths;
+
+            return;
+        }
+        if ($prepend) {
+            $this->prefixes[$first][$prefix] = array_merge(
+                (array) $paths,
+                $this->prefixes[$first][$prefix]
+            );
+        } else {
+            $this->prefixes[$first][$prefix] = array_merge(
+                $this->prefixes[$first][$prefix],
+                (array) $paths
+            );
+        }
+    }
+
+    /**
+     * Registers a set of classes, replacing any others previously set.
+     *
+     * @param string       $prefix The classes prefix
+     * @param array|string $paths  The location(s) of the classes
+     */
+    public function set($prefix, $paths)
+    {
+        if (!$prefix) {
+            $this->fallbackDirs = (array) $paths;
+
+            return;
+        }
+        $this->prefixes[substr($prefix, 0, 1)][$prefix] = (array) $paths;
+    }
+
+    /**
+     * Turns on searching the include path for class files.
+     *
+     * @param bool $useIncludePath
+     */
+    public function setUseIncludePath($useIncludePath)
+    {
+        $this->useIncludePath = $useIncludePath;
+    }
+
+    /**
+     * Can be used to check if the autoloader uses the include path to check
+     * for classes.
+     *
+     * @return bool
+     */
+    public function getUseIncludePath()
+    {
+        return $this->useIncludePath;
+    }
+
+    /**
+     * Registers this instance as an autoloader.
+     *
+     * @param bool $prepend Whether to prepend the autoloader or not
+     */
+    public function register($prepend = false)
+    {
+        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+    }
+
+    /**
+     * Unregisters this instance as an autoloader.
+     */
+    public function unregister()
+    {
+        spl_autoload_unregister(array($this, 'loadClass'));
+    }
+
+    /**
+     * Loads the given class or interface.
+     *
+     * @param  string    $class The name of the class
+     * @return bool|null True if loaded, null otherwise
+     */
+    public function loadClass($class)
+    {
+        if ($file = $this->findFile($class)) {
+            include $file;
+
+            return true;
+        }
+    }
+
+    /**
+     * Finds the path to the file where the class is defined.
+     *
+     * @param string $class The name of the class
+     *
+     * @return string|false The path if found, false otherwise
+     */
+    public function findFile($class)
+    {
+        // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
+        if ('\\' == $class[0]) {
+            $class = substr($class, 1);
+        }
+
+        if (isset($this->classMap[$class])) {
+            return $this->classMap[$class];
+        }
+
+        if (false !== $pos = strrpos($class, '\\')) {
+            // namespaced class name
+            $classPath = strtr(substr($class, 0, $pos), '\\', DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
+            $className = substr($class, $pos + 1);
+        } else {
+            // PEAR-like class name
+            $classPath = null;
+            $className = $class;
+        }
+
+        $classPath .= strtr($className, '_', DIRECTORY_SEPARATOR) . '.php';
+
+        $first = $class[0];
+        if (isset($this->prefixes[$first])) {
+            foreach ($this->prefixes[$first] as $prefix => $dirs) {
+                if (0 === strpos($class, $prefix)) {
+                    foreach ($dirs as $dir) {
+                        if (file_exists($dir . DIRECTORY_SEPARATOR . $classPath)) {
+                            return $dir . DIRECTORY_SEPARATOR . $classPath;
+                        }
+                    }
+                }
+            }
+        }
+
+        foreach ($this->fallbackDirs as $dir) {
+            if (file_exists($dir . DIRECTORY_SEPARATOR . $classPath)) {
+                return $dir . DIRECTORY_SEPARATOR . $classPath;
+            }
+        }
+
+        if ($this->useIncludePath && $file = stream_resolve_include_path($classPath)) {
+            return $file;
+        }
+
+        return $this->classMap[$class] = false;
+    }
+}
diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php
new file mode 100644 (file)
index 0000000..7574eb5
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+
+// autoload_classmap.php generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+    'Collator' => $vendorDir . '/symfony/intl/Symfony/Component/Intl/Resources/stubs/Collator.php',
+    'IntlDateFormatter' => $vendorDir . '/symfony/intl/Symfony/Component/Intl/Resources/stubs/IntlDateFormatter.php',
+    'Locale' => $vendorDir . '/symfony/intl/Symfony/Component/Intl/Resources/stubs/Locale.php',
+    'NumberFormatter' => $vendorDir . '/symfony/intl/Symfony/Component/Intl/Resources/stubs/NumberFormatter.php',
+);
diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php
new file mode 100644 (file)
index 0000000..a6552d0
--- /dev/null
@@ -0,0 +1,10 @@
+<?php
+
+// autoload_files.php generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+    $vendorDir . '/symfony/intl/Symfony/Component/Intl/Resources/stubs/functions.php',
+);
\ No newline at end of file
diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php
new file mode 100644 (file)
index 0000000..5d64b48
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+
+// autoload_namespaces.php generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+    'Twig_Extensions_' => array($vendorDir . '/twig/extensions/lib'),
+    'Twig_' => array($vendorDir . '/twig/twig/lib'),
+    'Twig\\Gettext' => array($vendorDir . '/umpirsky/twig-gettext-extractor'),
+    'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'),
+    'Symfony\\Component\\Routing\\' => array($vendorDir . '/symfony/routing'),
+    'Symfony\\Component\\PropertyAccess\\' => array($vendorDir . '/symfony/property-access'),
+    'Symfony\\Component\\OptionsResolver\\' => array($vendorDir . '/symfony/options-resolver'),
+    'Symfony\\Component\\Intl\\' => array($vendorDir . '/symfony/intl'),
+    'Symfony\\Component\\Icu\\' => array($vendorDir . '/symfony/icu'),
+    'Symfony\\Component\\Form\\' => array($vendorDir . '/symfony/form'),
+    'Symfony\\Component\\Filesystem\\' => array($vendorDir . '/symfony/filesystem'),
+    'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'),
+    'Symfony\\Bridge\\Twig\\' => array($vendorDir . '/symfony/twig-bridge'),
+);
diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php
new file mode 100644 (file)
index 0000000..f398fe3
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+
+// autoload_real.php generated by Composer
+
+class ComposerAutoloaderInit1c7743925d207055d2ad189b1f10a029
+{
+    private static $loader;
+
+    public static function loadClassLoader($class)
+    {
+        if ('Composer\Autoload\ClassLoader' === $class) {
+            require __DIR__ . '/ClassLoader.php';
+        }
+    }
+
+    public static function getLoader()
+    {
+        if (null !== self::$loader) {
+            return self::$loader;
+        }
+
+        spl_autoload_register(array('ComposerAutoloaderInit1c7743925d207055d2ad189b1f10a029', 'loadClassLoader'), true, true);
+        self::$loader = $loader = new \Composer\Autoload\ClassLoader();
+        spl_autoload_unregister(array('ComposerAutoloaderInit1c7743925d207055d2ad189b1f10a029', 'loadClassLoader'));
+
+        $vendorDir = dirname(__DIR__);
+        $baseDir = dirname($vendorDir);
+
+        $map = require __DIR__ . '/autoload_namespaces.php';
+        foreach ($map as $namespace => $path) {
+            $loader->set($namespace, $path);
+        }
+
+        $classMap = require __DIR__ . '/autoload_classmap.php';
+        if ($classMap) {
+            $loader->addClassMap($classMap);
+        }
+
+        $loader->register(true);
+
+        foreach (require __DIR__ . '/autoload_files.php' as $file) {
+            require $file;
+        }
+
+        return $loader;
+    }
+}
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
new file mode 100644 (file)
index 0000000..21a9b56
--- /dev/null
@@ -0,0 +1,747 @@
+[
+    {
+        "name": "twig/twig",
+        "version": "v1.13.2",
+        "version_normalized": "1.13.2.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/fabpot/Twig.git",
+            "reference": "v1.13.2"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/fabpot/Twig/zipball/v1.13.2",
+            "reference": "v1.13.2",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.2.4"
+        },
+        "time": "2013-08-03 15:35:31",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.13-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-0": {
+                "Twig_": "lib/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "BSD-3-Clause"
+        ],
+        "authors": [
+            {
+                "name": "Fabien Potencier",
+                "email": "fabien@symfony.com"
+            },
+            {
+                "name": "Armin Ronacher",
+                "email": "armin.ronacher@active-4.com"
+            }
+        ],
+        "description": "Twig, the flexible, fast, and secure template language for PHP",
+        "homepage": "http://twig.sensiolabs.org",
+        "keywords": [
+            "templating"
+        ]
+    },
+    {
+        "name": "twig/extensions",
+        "version": "dev-master",
+        "version_normalized": "9999999-dev",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/fabpot/Twig-extensions.git",
+            "reference": "f5b0c84f3699e494c84ee627d7d583e115d2c4a2"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/fabpot/Twig-extensions/zipball/f5b0c84f3699e494c84ee627d7d583e115d2c4a2",
+            "reference": "f5b0c84f3699e494c84ee627d7d583e115d2c4a2",
+            "shasum": ""
+        },
+        "require": {
+            "twig/twig": "~1.0"
+        },
+        "time": "2013-07-02 11:21:55",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.0.x-dev"
+            }
+        },
+        "installation-source": "source",
+        "autoload": {
+            "psr-0": {
+                "Twig_Extensions_": "lib/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Fabien Potencier",
+                "email": "fabien@symfony.com"
+            }
+        ],
+        "description": "Common additional features for Twig that do not directly belong in core",
+        "homepage": "https://github.com/fabpot/Twig-extensions",
+        "keywords": [
+            "debug",
+            "i18n",
+            "text"
+        ]
+    },
+    {
+        "name": "symfony/icu",
+        "version": "v1.0.0",
+        "version_normalized": "1.0.0.0",
+        "target-dir": "Symfony/Component/Icu",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/symfony/Icu.git",
+            "reference": "v1.0.0"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/symfony/Icu/zipball/v1.0.0",
+            "reference": "v1.0.0",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.3",
+            "symfony/intl": ">=2.3,<3.0"
+        },
+        "time": "2013-06-03 18:32:07",
+        "type": "library",
+        "installation-source": "dist",
+        "autoload": {
+            "psr-0": {
+                "Symfony\\Component\\Icu\\": ""
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Symfony Community",
+                "homepage": "http://symfony.com/contributors"
+            },
+            {
+                "name": "Bernhard Schussek",
+                "email": "bschussek@gmail.com"
+            }
+        ],
+        "description": "Contains an excerpt of the ICU data and classes to load it.",
+        "homepage": "http://symfony.com",
+        "keywords": [
+            "icu",
+            "intl"
+        ]
+    },
+    {
+        "name": "symfony/intl",
+        "version": "v2.3.2",
+        "version_normalized": "2.3.2.0",
+        "target-dir": "Symfony/Component/Intl",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/symfony/Intl.git",
+            "reference": "v2.3.2"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/symfony/Intl/zipball/v2.3.2",
+            "reference": "v2.3.2",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.3",
+            "symfony/icu": "~1.0-RC"
+        },
+        "require-dev": {
+            "symfony/filesystem": ">=2.1"
+        },
+        "suggest": {
+            "ext-intl": "to use the component with locales other than \"en\""
+        },
+        "time": "2013-07-08 13:00:35",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "2.3-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-0": {
+                "Symfony\\Component\\Intl\\": ""
+            },
+            "classmap": [
+                "Symfony/Component/Intl/Resources/stubs"
+            ],
+            "files": [
+                "Symfony/Component/Intl/Resources/stubs/functions.php"
+            ]
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Symfony Community",
+                "homepage": "http://symfony.com/contributors"
+            },
+            {
+                "name": "Igor Wiedler",
+                "email": "igor@wiedler.ch",
+                "homepage": "http://wiedler.ch/igor/"
+            },
+            {
+                "name": "Bernhard Schussek",
+                "email": "bschussek@gmail.com"
+            },
+            {
+                "name": "Eriksen Costa",
+                "email": "eriksen.costa@infranology.com.br"
+            }
+        ],
+        "description": "A PHP replacement layer for the C intl extension that includes additional data from the ICU library.",
+        "homepage": "http://symfony.com",
+        "keywords": [
+            "i18n",
+            "icu",
+            "internationalization",
+            "intl",
+            "l10n",
+            "localization"
+        ]
+    },
+    {
+        "name": "symfony/property-access",
+        "version": "v2.3.2",
+        "version_normalized": "2.3.2.0",
+        "target-dir": "Symfony/Component/PropertyAccess",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/symfony/PropertyAccess.git",
+            "reference": "v2.3.2"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/symfony/PropertyAccess/zipball/v2.3.2",
+            "reference": "v2.3.2",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.3"
+        },
+        "time": "2013-07-01 12:24:43",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "2.3-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-0": {
+                "Symfony\\Component\\PropertyAccess\\": ""
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Fabien Potencier",
+                "email": "fabien@symfony.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "http://symfony.com/contributors"
+            }
+        ],
+        "description": "Symfony PropertyAccess Component",
+        "homepage": "http://symfony.com",
+        "keywords": [
+            "access",
+            "array",
+            "extraction",
+            "index",
+            "injection",
+            "object",
+            "property",
+            "property path",
+            "reflection"
+        ]
+    },
+    {
+        "name": "symfony/options-resolver",
+        "version": "v2.3.2",
+        "version_normalized": "2.3.2.0",
+        "target-dir": "Symfony/Component/OptionsResolver",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/symfony/OptionsResolver.git",
+            "reference": "v2.3.2"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/symfony/OptionsResolver/zipball/v2.3.2",
+            "reference": "v2.3.2",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.3"
+        },
+        "time": "2013-04-11 06:50:46",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "2.3-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-0": {
+                "Symfony\\Component\\OptionsResolver\\": ""
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Fabien Potencier",
+                "email": "fabien@symfony.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "http://symfony.com/contributors"
+            }
+        ],
+        "description": "Symfony OptionsResolver Component",
+        "homepage": "http://symfony.com",
+        "keywords": [
+            "config",
+            "configuration",
+            "options"
+        ]
+    },
+    {
+        "name": "symfony/event-dispatcher",
+        "version": "v2.3.2",
+        "version_normalized": "2.3.2.0",
+        "target-dir": "Symfony/Component/EventDispatcher",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/symfony/EventDispatcher.git",
+            "reference": "v2.3.2"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/v2.3.2",
+            "reference": "v2.3.2",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.3"
+        },
+        "require-dev": {
+            "symfony/dependency-injection": "~2.0"
+        },
+        "suggest": {
+            "symfony/dependency-injection": "",
+            "symfony/http-kernel": ""
+        },
+        "time": "2013-05-13 14:36:40",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "2.3-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-0": {
+                "Symfony\\Component\\EventDispatcher\\": ""
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Fabien Potencier",
+                "email": "fabien@symfony.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "http://symfony.com/contributors"
+            }
+        ],
+        "description": "Symfony EventDispatcher Component",
+        "homepage": "http://symfony.com"
+    },
+    {
+        "name": "symfony/form",
+        "version": "v2.3.2",
+        "version_normalized": "2.3.2.0",
+        "target-dir": "Symfony/Component/Form",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/symfony/Form.git",
+            "reference": "v2.3.2"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/symfony/Form/zipball/v2.3.2",
+            "reference": "v2.3.2",
+            "shasum": ""
+        },
+        "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/http-foundation": "~2.2",
+            "symfony/validator": "~2.2"
+        },
+        "suggest": {
+            "symfony/http-foundation": "",
+            "symfony/validator": ""
+        },
+        "time": "2013-07-01 12:24:43",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "2.3-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-0": {
+                "Symfony\\Component\\Form\\": ""
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Fabien Potencier",
+                "email": "fabien@symfony.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "http://symfony.com/contributors"
+            }
+        ],
+        "description": "Symfony Form Component",
+        "homepage": "http://symfony.com"
+    },
+    {
+        "name": "symfony/translation",
+        "version": "v2.3.2",
+        "version_normalized": "2.3.2.0",
+        "target-dir": "Symfony/Component/Translation",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/symfony/Translation.git",
+            "reference": "v2.3.2"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/symfony/Translation/zipball/v2.3.2",
+            "reference": "v2.3.2",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.3"
+        },
+        "require-dev": {
+            "symfony/config": "~2.0",
+            "symfony/yaml": "~2.2"
+        },
+        "suggest": {
+            "symfony/config": "",
+            "symfony/yaml": ""
+        },
+        "time": "2013-05-13 14:36:40",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "2.3-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-0": {
+                "Symfony\\Component\\Translation\\": ""
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Fabien Potencier",
+                "email": "fabien@symfony.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "http://symfony.com/contributors"
+            }
+        ],
+        "description": "Symfony Translation Component",
+        "homepage": "http://symfony.com"
+    },
+    {
+        "name": "symfony/filesystem",
+        "version": "v2.3.2",
+        "version_normalized": "2.3.2.0",
+        "target-dir": "Symfony/Component/Filesystem",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/symfony/Filesystem.git",
+            "reference": "v2.3.2"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/symfony/Filesystem/zipball/v2.3.2",
+            "reference": "v2.3.2",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.3"
+        },
+        "time": "2013-06-04 15:02:05",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "2.3-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-0": {
+                "Symfony\\Component\\Filesystem\\": ""
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Fabien Potencier",
+                "email": "fabien@symfony.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "http://symfony.com/contributors"
+            }
+        ],
+        "description": "Symfony Filesystem Component",
+        "homepage": "http://symfony.com"
+    },
+    {
+        "name": "symfony/routing",
+        "version": "v2.3.2",
+        "version_normalized": "2.3.2.0",
+        "target-dir": "Symfony/Component/Routing",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/symfony/Routing.git",
+            "reference": "v2.3.2"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/symfony/Routing/zipball/v2.3.2",
+            "reference": "v2.3.2",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.3"
+        },
+        "require-dev": {
+            "doctrine/common": "~2.2",
+            "psr/log": "~1.0",
+            "symfony/config": "~2.2",
+            "symfony/yaml": "~2.0"
+        },
+        "suggest": {
+            "doctrine/common": "",
+            "symfony/config": "",
+            "symfony/yaml": ""
+        },
+        "time": "2013-06-23 08:16:02",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "2.3-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-0": {
+                "Symfony\\Component\\Routing\\": ""
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Fabien Potencier",
+                "email": "fabien@symfony.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "http://symfony.com/contributors"
+            }
+        ],
+        "description": "Symfony Routing Component",
+        "homepage": "http://symfony.com"
+    },
+    {
+        "name": "symfony/twig-bridge",
+        "version": "v2.3.2",
+        "version_normalized": "2.3.2.0",
+        "target-dir": "Symfony/Bridge/Twig",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/symfony/TwigBridge.git",
+            "reference": "v2.3.2"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/symfony/TwigBridge/zipball/v2.3.2",
+            "reference": "v2.3.2",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.3",
+            "twig/twig": "~1.11"
+        },
+        "require-dev": {
+            "symfony/form": "2.2.*",
+            "symfony/http-kernel": "~2.2",
+            "symfony/routing": "~2.2",
+            "symfony/security": "~2.0",
+            "symfony/templating": "~2.1",
+            "symfony/translation": "~2.2",
+            "symfony/yaml": "~2.0"
+        },
+        "suggest": {
+            "symfony/form": "",
+            "symfony/http-kernel": "",
+            "symfony/routing": "",
+            "symfony/security": "",
+            "symfony/templating": "",
+            "symfony/translation": "",
+            "symfony/yaml": ""
+        },
+        "time": "2013-05-16 10:19:58",
+        "type": "symfony-bridge",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "2.3-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-0": {
+                "Symfony\\Bridge\\Twig\\": ""
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Fabien Potencier",
+                "email": "fabien@symfony.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "http://symfony.com/contributors"
+            }
+        ],
+        "description": "Symfony Twig Bridge",
+        "homepage": "http://symfony.com"
+    },
+    {
+        "name": "umpirsky/twig-gettext-extractor",
+        "version": "1.1.3",
+        "version_normalized": "1.1.3.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/umpirsky/Twig-Gettext-Extractor.git",
+            "reference": "1.1.3"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/umpirsky/Twig-Gettext-Extractor/zipball/1.1.3",
+            "reference": "1.1.3",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.3",
+            "symfony/filesystem": ">=2.0,<3.0",
+            "symfony/form": ">=2.0,<3.0",
+            "symfony/routing": ">=2.0,<3.0",
+            "symfony/translation": ">=2.0,<3.0",
+            "symfony/twig-bridge": ">=2.0,<3.0",
+            "twig/extensions": "1.0.*",
+            "twig/twig": ">=1.2.0,<2.0-dev"
+        },
+        "require-dev": {
+            "symfony/config": "2.1.*"
+        },
+        "time": "2013-02-14 16:41:48",
+        "bin": [
+            "twig-gettext-extractor"
+        ],
+        "type": "application",
+        "installation-source": "dist",
+        "autoload": {
+            "psr-0": {
+                "Twig\\Gettext": "."
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Саша Стаменковић",
+                "email": "umpirsky@gmail.com",
+                "homepage": "http://umpirsky.com"
+            }
+        ],
+        "description": "The Twig Gettext Extractor is Poedit friendly tool which extracts translations from twig templates."
+    }
+]
diff --git a/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.gitignore b/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.gitignore
new file mode 100644 (file)
index 0000000..44de97a
--- /dev/null
@@ -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 (file)
index 0000000..536c5ac
--- /dev/null
@@ -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 (file)
index 0000000..9448ed4
--- /dev/null
@@ -0,0 +1,202 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\EventDispatcher;
+
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Lazily loads listeners and subscribers from the dependency injection
+ * container
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ * @author Jordan Alliot <jordan.alliot@gmail.com>
+ */
+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 (file)
index 0000000..a67a979
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\EventDispatcher\Debug;
+
+/**
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+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 (file)
index 0000000..42f09ea
--- /dev/null
@@ -0,0 +1,121 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <guilhermeblanco@hotmail.com>
+ * @author  Jonathan Wage <jonwage@gmail.com>
+ * @author  Roman Borschel <roman@code-factory.org>
+ * @author  Bernhard Schussek <bschussek@gmail.com>
+ *
+ * @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 (file)
index 0000000..eb1fb59
--- /dev/null
@@ -0,0 +1,185 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <guilhermeblanco@hotmail.com>
+ * @author  Jonathan Wage <jonwage@gmail.com>
+ * @author  Roman Borschel <roman@code-factory.org>
+ * @author  Bernhard Schussek <bschussek@gmail.com>
+ * @author  Fabien Potencier <fabien@symfony.com>
+ * @author  Jordi Boggiano <j.boggiano@seld.be>
+ * @author  Jordan Alliot <jordan.alliot@gmail.com>
+ *
+ * @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 (file)
index 0000000..7aead23
--- /dev/null
@@ -0,0 +1,96 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ *
+ * @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 (file)
index 0000000..080f892
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <guilhermeblanco@hotmail.com>
+ * @author  Jonathan Wage <jonwage@gmail.com>
+ * @author  Roman Borschel <roman@code-factory.org>
+ * @author  Bernhard Schussek <bschussek@gmail.com>
+ *
+ * @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 (file)
index 0000000..3a5efcf
--- /dev/null
@@ -0,0 +1,186 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\EventDispatcher;
+
+/**
+ * Event encapsulation class.
+ *
+ * Encapsulates events thus decoupling the observer from the subject they encapsulate.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+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 (file)
index 0000000..b70b81a
--- /dev/null
@@ -0,0 +1,92 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\EventDispatcher;
+
+/**
+ * A read-only proxy for an event dispatcher.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..88a57f8
--- /dev/null
@@ -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 (file)
index 0000000..11f6b18
--- /dev/null
@@ -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 (file)
index 0000000..71f3ad0
--- /dev/null
@@ -0,0 +1,257 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..ad7e448
--- /dev/null
@@ -0,0 +1,320 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..52aa9ad
--- /dev/null
@@ -0,0 +1,84 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..8dd6f5b
--- /dev/null
@@ -0,0 +1,140 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..6402f89
--- /dev/null
@@ -0,0 +1,106 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\EventDispatcher\Tests;
+
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\EventDispatcher\ImmutableEventDispatcher;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..1db2ecf
--- /dev/null
@@ -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 (file)
index 0000000..0c3de4f
--- /dev/null
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         processIsolation="false"
+         stopOnFailure="false"
+         syntaxCheck="false"
+         bootstrap="vendor/autoload.php"
+>
+    <testsuites>
+        <testsuite name="Symfony EventDispatcher Component Test Suite">
+            <directory>./Tests/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./</directory>
+            <exclude>
+                <directory>./Resources</directory>
+                <directory>./Tests</directory>
+                <directory>./vendor</directory>
+            </exclude>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/vendor/symfony/filesystem/Symfony/Component/Filesystem/.gitignore b/vendor/symfony/filesystem/Symfony/Component/Filesystem/.gitignore
new file mode 100644 (file)
index 0000000..44de97a
--- /dev/null
@@ -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 (file)
index 0000000..e6aee66
--- /dev/null
@@ -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 (file)
index 0000000..bc9748d
--- /dev/null
@@ -0,0 +1,24 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Filesystem\Exception;
+
+/**
+ * Exception interface for all exceptions thrown by the component.
+ *
+ * @author Romain Neutron <imprec@gmail.com>
+ *
+ * @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 (file)
index 0000000..5b27e66
--- /dev/null
@@ -0,0 +1,24 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Filesystem\Exception;
+
+/**
+ * Exception class thrown when a filesystem operation failure happens
+ *
+ * @author Romain Neutron <imprec@gmail.com>
+ *
+ * @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 (file)
index 0000000..6e015b4
--- /dev/null
@@ -0,0 +1,471 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Filesystem;
+
+use Symfony\Component\Filesystem\Exception\IOException;
+
+/**
+ * Provides basic utility to manipulate the file system.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+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 (file)
index 0000000..88a57f8
--- /dev/null
@@ -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 (file)
index 0000000..94ac146
--- /dev/null
@@ -0,0 +1,45 @@
+Filesystem Component
+====================
+
+Filesystem provides basic utility to manipulate the file system:
+
+```php
+<?php
+
+use Symfony\Component\Filesystem\Filesystem;
+
+$filesystem = new Filesystem();
+
+$filesystem->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 (file)
index 0000000..02969f3
--- /dev/null
@@ -0,0 +1,982 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..167dd50
--- /dev/null
@@ -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 (file)
index 0000000..ef0bf95
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         processIsolation="false"
+         stopOnFailure="false"
+         syntaxCheck="false"
+         bootstrap="vendor/autoload.php"
+>
+    <testsuites>
+        <testsuite name="Symfony Filesystem Component Test Suite">
+            <directory>./Tests/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./</directory>
+            <exclude>
+                <directory>./Tests</directory>
+            </exclude>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/vendor/symfony/form/Symfony/Component/Form/.gitignore b/vendor/symfony/form/Symfony/Component/Form/.gitignore
new file mode 100644 (file)
index 0000000..44de97a
--- /dev/null
@@ -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 (file)
index 0000000..4db77b9
--- /dev/null
@@ -0,0 +1,195 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+use Symfony\Component\Form\Exception\InvalidArgumentException;
+use Symfony\Component\Form\Exception\UnexpectedTypeException;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..347be10
--- /dev/null
@@ -0,0 +1,206 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+/**
+ * Default implementation of {@link FormRendererEngineInterface}.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..6f7f5da
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+use Symfony\Component\OptionsResolver\OptionsResolverInterface;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..351c800
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+use Symfony\Component\OptionsResolver\OptionsResolverInterface;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..6e12ba1
--- /dev/null
@@ -0,0 +1,436 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+use Symfony\Component\Form\Exception\AlreadySubmittedException;
+use Symfony\Component\Form\Exception\BadMethodCallException;
+
+/**
+ * A form button.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..3addedb
--- /dev/null
@@ -0,0 +1,864 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..dd5117c
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+/**
+ * A type that should be converted into a {@link Button} instance.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..a696c7b
--- /dev/null
@@ -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 (file)
index 0000000..1dfe890
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..893f02d
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+/**
+ * A clickable form element.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..6e03168
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..6567da2
--- /dev/null
@@ -0,0 +1,77 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+use Symfony\Component\Form\Exception\TransformationFailedException;
+
+/**
+ * Transforms a value between different representations.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..7ef0ca0
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..7be2124
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Exception;
+
+/**
+ * Thrown when an operation is called that is not acceptable after submitting
+ * a form.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..27649dd
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Exception;
+
+/**
+ * Base BadMethodCallException for the Form component.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..a696849
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..d455932
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Exception;
+
+/**
+ * Base ExceptionInterface for the Form component.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..a270e0c
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Exception;
+
+/**
+ * Base InvalidArgumentException for the Form component.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..daa0c42
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..8487802
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Exception;
+
+/**
+ * Base LogicException for Form component.
+ *
+ * @author Alexander Kotynia <aleksander.kot@gmail.com>
+ */
+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 (file)
index 0000000..44d3116
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Exception;
+
+/**
+ * Base OutOfBoundsException for Form component.
+ *
+ * @author Alexander Kotynia <aleksander.kot@gmail.com>
+ */
+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 (file)
index 0000000..0af48a4
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Exception;
+
+/**
+ * Base RuntimeException for the Form component.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..f9b51d6
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..d32896e
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Exception;
+
+/**
+ * Indicates a value transformation error.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..474e244
--- /dev/null
@@ -0,0 +1,20 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..f9d381c
--- /dev/null
@@ -0,0 +1,510 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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.
+ *
+ * <code>
+ * $choices = array(true, false);
+ * $labels = array('Agree', 'Disagree');
+ * $choiceList = new ChoiceList($choices, $labels);
+ * </code>
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..099ace8
--- /dev/null
@@ -0,0 +1,149 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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:
+     *
+     * <source>
+     * array(
+     *     'Group 1' => array(
+     *         10 => ChoiceView object,
+     *         20 => ChoiceView object,
+     *     ),
+     *     'Group 2' => array(
+     *         30 => ChoiceView object,
+     *     ),
+     * )
+     * </source>
+     *
+     * @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:
+     *
+     * <source>
+     * array(
+     *     'Group 1' => array(
+     *         10 => ChoiceView object,
+     *         20 => ChoiceView object,
+     *     ),
+     *     'Group 2' => array(
+     *         30 => ChoiceView object,
+     *     ),
+     * )
+     * </source>
+     *
+     * @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 (file)
index 0000000..996f900
--- /dev/null
@@ -0,0 +1,149 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..0a15388
--- /dev/null
@@ -0,0 +1,184 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+*
+* (c) Fabien Potencier <fabien@symfony.com>
+*
+* For the full copyright and license information, please view the LICENSE
+* file that was distributed with this source code.
+*/
+
+namespace Symfony\Component\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).
+ *
+ * <code>
+ * $choices = array($user1, $user2);
+ *
+ * // call getName() to determine the choice labels
+ * $choiceList = new ObjectChoiceList($choices, 'name');
+ * </code>
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..914dbe5
--- /dev/null
@@ -0,0 +1,164 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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.
+ *
+ * <code>
+ * $choiceList = new SimpleChoiceList(array(
+ *     'creditcard' => 'Credit card payment',
+ *     'cash' => 'Cash payment',
+ * ));
+ * </code>
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..bbcac4b
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..d8bd9c7
--- /dev/null
@@ -0,0 +1,92 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..fc080f2
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Core\DataTransformer;
+
+use Symfony\Component\Form\DataTransformerInterface;
+use Symfony\Component\Form\Exception\TransformationFailedException;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..e4e8932
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..95e7332
--- /dev/null
@@ -0,0 +1,85 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ * @author Florian Eckerstorfer <florian@eckerstorfer.org>
+ */
+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 (file)
index 0000000..79b3f7a
--- /dev/null
@@ -0,0 +1,118 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..5a81855
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..a13c0d4
--- /dev/null
@@ -0,0 +1,117 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..4492865
--- /dev/null
@@ -0,0 +1,83 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..9cc185e
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..34af282
--- /dev/null
@@ -0,0 +1,184 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ * @author Florian Eckerstorfer <florian@eckerstorfer.org>
+ */
+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 (file)
index 0000000..d755e48
--- /dev/null
@@ -0,0 +1,169 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ * @author Florian Eckerstorfer <florian@eckerstorfer.org>
+ */
+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 (file)
index 0000000..0eb0742
--- /dev/null
@@ -0,0 +1,82 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Core\DataTransformer;
+
+use Symfony\Component\Form\Exception\TransformationFailedException;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..131f45c
--- /dev/null
@@ -0,0 +1,231 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ * @author Florian Eckerstorfer <florian@eckerstorfer.org>
+ */
+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<day>[djDl])|' .
+                    '(?P<month>[FMmn])|' .
+                    '(?P<year>[Yy])|' .
+                    '(?P<hour>[ghGH])|' .
+                    '(?P<minute>i)|' .
+                    '(?P<second>s)|' .
+                    '(?P<dayofyear>z)|' .
+                    '(?P<timestamp>U)|' .
+                    '[^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 (file)
index 0000000..d2ca660
--- /dev/null
@@ -0,0 +1,89 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Core\DataTransformer;
+
+use Symfony\Component\Form\Exception\TransformationFailedException;
+
+/**
+ * Transforms between a timestamp and a DateTime object
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ * @author Florian Eckerstorfer <florian@eckerstorfer.org>
+ */
+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 (file)
index 0000000..6bb48a9
--- /dev/null
@@ -0,0 +1,53 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..5b8e9d9
--- /dev/null
@@ -0,0 +1,90 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Core\DataTransformer;
+
+use Symfony\Component\Form\Exception\TransformationFailedException;
+
+/**
+ * Transforms between a normalized format and a localized money string.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ * @author Florian Eckerstorfer <florian@eckerstorfer.org>
+ */
+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 (file)
index 0000000..b0c59b3
--- /dev/null
@@ -0,0 +1,184 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ * @author Florian Eckerstorfer <florian@eckerstorfer.org>
+ */
+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 (file)
index 0000000..e099d43
--- /dev/null
@@ -0,0 +1,149 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ * @author Florian Eckerstorfer <florian@eckerstorfer.org>
+ */
+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 (file)
index 0000000..c34a013
--- /dev/null
@@ -0,0 +1,91 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Core\DataTransformer;
+
+use Symfony\Component\Form\DataTransformerInterface;
+use Symfony\Component\Form\Exception\TransformationFailedException;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..1f62e06
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..bf03792
--- /dev/null
@@ -0,0 +1,66 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..e25dacf
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..4d0bdfa
--- /dev/null
@@ -0,0 +1,137 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..f1c39db
--- /dev/null
@@ -0,0 +1,173 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..cbe6e0a
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..79333a6
--- /dev/null
@@ -0,0 +1,121 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..5314c14
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..3569963
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Core\Type;
+
+use Symfony\Component\Form\ButtonTypeInterface;
+
+/**
+ * A form button.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..214e581
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..9a3fdef
--- /dev/null
@@ -0,0 +1,274 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..0cb3af1
--- /dev/null
@@ -0,0 +1,103 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..3482ba6
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..3a925e3
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..a612b6f
--- /dev/null
@@ -0,0 +1,281 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..93d3502
--- /dev/null
@@ -0,0 +1,309 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..26652ef
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..2c09da6
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..0c39d3e
--- /dev/null
@@ -0,0 +1,214 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..bd4fa89
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..b224cac
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..37b2bf3
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..c68c561
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..9e36f9c
--- /dev/null
@@ -0,0 +1,111 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..beb3c89
--- /dev/null
@@ -0,0 +1,66 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..5a5b163
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..b1df943
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..dfa7c7d
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..9a3cd14
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..cf55f7c
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Core\Type;
+
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\ButtonTypeInterface;
+
+/**
+ * A reset button.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..bf82972
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..6d160b9
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..1150326
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..0e749b1
--- /dev/null
@@ -0,0 +1,43 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..d7a2a9e
--- /dev/null
@@ -0,0 +1,225 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..cd4a2ad
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..27749b1
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..97cdd21
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Core\View;
+
+/**
+ * Represents a choice in templates.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..f9d9e40
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..7143b13
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..5354886
--- /dev/null
@@ -0,0 +1,78 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..ea1fa58
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..547e9d7
--- /dev/null
@@ -0,0 +1,115 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..336cf04
--- /dev/null
@@ -0,0 +1,129 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..6637ac8
--- /dev/null
@@ -0,0 +1,101 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..6205b98
--- /dev/null
@@ -0,0 +1,91 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ *
+ * @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 (file)
index 0000000..08bd89c
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\HttpFoundation;
+
+use Symfony\Component\Form\AbstractExtension;
+
+/**
+ * Integrates the HttpFoundation component with the Form library.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..cc48515
--- /dev/null
@@ -0,0 +1,79 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..9b09b05
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..573cb51
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..c1dda60
--- /dev/null
@@ -0,0 +1,125 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Templating;
+
+use Symfony\Component\Form\AbstractRendererEngine;
+use Symfony\Component\Form\FormView;
+use Symfony\Component\Templating\EngineInterface;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..87e3329
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Validator\Constraints;
+
+use Symfony\Component\Validator\Constraint;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..bad5a00
--- /dev/null
@@ -0,0 +1,236 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..1414753
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..7c5e678
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..344bdda
--- /dev/null
@@ -0,0 +1,84 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..858ff0f
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Validator\Type;
+
+use Symfony\Component\Form\AbstractTypeExtension;
+use Symfony\Component\OptionsResolver\Options;
+use Symfony\Component\OptionsResolver\OptionsResolverInterface;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..5aad67f
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Validator\Type;
+
+use Symfony\Component\Form\AbstractTypeExtension;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..fab6ac2
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Validator\Util;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..9cff22a
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..dcd9cc5
--- /dev/null
@@ -0,0 +1,286 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..7b96efb
--- /dev/null
@@ -0,0 +1,106 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Validator\ViolationMapper;
+
+use Symfony\Component\Form\FormInterface;
+use Symfony\Component\Form\Exception\ErrorMappingException;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..ef5c9fa
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Validator\ViolationMapper;
+
+use Symfony\Component\Form\FormInterface;
+use Symfony\Component\PropertyAccess\PropertyPath;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..8a7636c
--- /dev/null
@@ -0,0 +1,299 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..eb8907f
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Validator\ViolationMapper;
+
+use Symfony\Component\Form\FormInterface;
+use Symfony\Component\Validator\ConstraintViolation;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..06d0919
--- /dev/null
@@ -0,0 +1,250 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Validator\ViolationMapper;
+
+use Symfony\Component\Form\Exception\OutOfBoundsException;
+use Symfony\Component\PropertyAccess\PropertyPath;
+use Symfony\Component\PropertyAccess\PropertyPathInterface;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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:
+     *
+     * <code>
+     * children[address].children[office].data.street
+     * </code>
+     *
+     * 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 (file)
index 0000000..50baa45
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Validator\ViolationMapper;
+
+use Symfony\Component\PropertyAccess\PropertyPathIterator;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..3513527
--- /dev/null
@@ -0,0 +1,1046 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <fabien@symfony.com>
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..2caefe4
--- /dev/null
@@ -0,0 +1,275 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..1dc4a64
--- /dev/null
@@ -0,0 +1,87 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..1015da4
--- /dev/null
@@ -0,0 +1,919 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..62d12c0
--- /dev/null
@@ -0,0 +1,287 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..576fcd8
--- /dev/null
@@ -0,0 +1,243 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+/**
+ * The configuration of a {@link Form} object.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..343165c
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+/**
+ * Wraps errors in forms
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..57cebad
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+use Symfony\Component\EventDispatcher\Event;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..6c4efc5
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..a67055b
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..a5fd9cc
--- /dev/null
@@ -0,0 +1,156 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..10383e8
--- /dev/null
@@ -0,0 +1,162 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+/**
+ * The default implementation of FormFactoryBuilderInterface.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..9370c57
--- /dev/null
@@ -0,0 +1,108 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+/**
+ * A builder for FormFactoryInterface objects.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..31c46b5
--- /dev/null
@@ -0,0 +1,109 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..5a852e8
--- /dev/null
@@ -0,0 +1,288 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+/**
+ * A form group bundling multiple forms in a hierarchical structure.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..0267a56
--- /dev/null
@@ -0,0 +1,180 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..16cd938
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+/**
+ * The central registry of the Form component.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..09b0105
--- /dev/null
@@ -0,0 +1,304 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 <div> 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 (= "_<id>_<section>" 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 (file)
index 0000000..e06824e
--- /dev/null
@@ -0,0 +1,150 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+/**
+ * Adapter for rendering form templates with a specific templating engine.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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:
+     *
+     * <code>
+     * form_widget
+     * text_widget
+     * url_widget
+     * </code>
+     *
+     * 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:
+     *
+     * <code>
+     * form_widget
+     * text_widget
+     * url_widget
+     * </code>
+     *
+     * 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 (file)
index 0000000..848e712
--- /dev/null
@@ -0,0 +1,103 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+/**
+ * Renders a form into HTML.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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.
+     *
+     * <code>
+     * <input type="hidden" name="token" value="<?php $renderer->renderCsrfToken('rm_user_'.$user->getId()) ?>">
+     * </code>
+     *
+     * Check the token in your action using the same intention.
+     *
+     * <code>
+     * $csrfProvider = $this->get('form.csrf_provider');
+     * if (!$csrfProvider->isCsrfTokenValid('rm_user_'.$user->getId(), $token)) {
+     *     throw new \RuntimeException('CSRF attack detected.');
+     * }
+     * </code>
+     *
+     * @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 (file)
index 0000000..9866b28
--- /dev/null
@@ -0,0 +1,75 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+use Symfony\Component\OptionsResolver\OptionsResolverInterface;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..c7f8ece
--- /dev/null
@@ -0,0 +1,104 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..e8b603f
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..bcef73c
--- /dev/null
@@ -0,0 +1,95 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+use Symfony\Component\OptionsResolver\OptionsResolverInterface;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..1f53ec6
--- /dev/null
@@ -0,0 +1,159 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+use Symfony\Component\Form\Exception\BadMethodCallException;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..c949c1f
--- /dev/null
@@ -0,0 +1,185 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+use Symfony\Component\Form\Extension\Core\CoreExtension;
+
+/**
+ * Entry point of the Form component.
+ *
+ * Use this class to conveniently create new form factories:
+ *
+ * <code>
+ * 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();
+ * </code>
+ *
+ * You can also add custom extensions to the form factory:
+ *
+ * <code>
+ * $formFactory = Forms::createFormFactoryBuilder()
+ *     ->addExtension(new AcmeExtension())
+ *     ->getFormFactory();
+ * </code>
+ *
+ * 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:
+ *
+ * <code>
+ * $formFactory = Forms::createFormFactoryBuilder()
+ *     ->addType(new PersonType())
+ *     ->addType(new PhoneNumberType())
+ *     ->addTypeExtension(new FormTypeHelpTextExtension())
+ *     ->getFormFactory();
+ * </code>
+ *
+ * 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:
+ *
+ * <code>
+ * 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();
+ * </code>
+ *
+ * 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:
+ *
+ * <code>
+ * 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();
+ * </code>
+ *
+ * Support for the Validator component is provided by ValidatorExtension.
+ * This extension needs a validator object to function properly:
+ *
+ * <code>
+ * use Symfony\Component\Validator\Validation;
+ * use Symfony\Component\Form\Extension\Validator\ValidatorExtension;
+ *
+ * $validator = Validation::createValidator();
+ * $formFactory = Forms::createFormFactoryBuilder()
+ *     ->addExtension(new ValidatorExtension($validator))
+ *     ->getFormFactory();
+ * </code>
+ *
+ * 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 "<div>" tags:
+ *
+ * <code>
+ * use Symfony\Component\Form\Extension\Templating\TemplatingExtension;
+ *
+ * $formFactory = Forms::createFormFactoryBuilder()
+ *     ->addExtension(new TemplatingExtension($engine, null, array(
+ *         'FrameworkBundle:Form',
+ *     )))
+ *     ->getFormFactory();
+ * </code>
+ *
+ * The next example shows how to include the "<table>" layout:
+ *
+ * <code>
+ * use Symfony\Component\Form\Extension\Templating\TemplatingExtension;
+ *
+ * $formFactory = Forms::createFormFactoryBuilder()
+ *     ->addExtension(new TemplatingExtension($engine, null, array(
+ *         'FrameworkBundle:Form',
+ *         'FrameworkBundle:FormTable',
+ *     )))
+ *     ->getFormFactory();
+ * </code>
+ *
+ * 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:
+ *
+ * <code>
+ * 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();
+ * </code>
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..b33c3d8
--- /dev/null
@@ -0,0 +1,113 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..3241e60
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Guess;
+
+/**
+ * Contains a guessed class name and a list of options for creating an instance
+ * of that class
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..2e3333b
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Guess;
+
+/**
+ * Contains a guessed value
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..88a57f8
--- /dev/null
@@ -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 (file)
index 0000000..aaa4e4c
--- /dev/null
@@ -0,0 +1,194 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..2d3e9ef
--- /dev/null
@@ -0,0 +1,97 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+use Symfony\Component\Form\Exception\InvalidArgumentException;
+
+/**
+ * A form extension with preloaded types, type exceptions and type guessers.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..7bfff7f
--- /dev/null
@@ -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 (file)
index 0000000..d0a58e6
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+/**
+ * Submits forms if they were submitted.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..47d4355
--- /dev/null
@@ -0,0 +1,284 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..d93d1c0
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..f0ec233
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..c999bcd
--- /dev/null
@@ -0,0 +1,106 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+/**
+ * A wrapper for a form type and its extensions.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..2f2364b
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" ?>
+
+<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
+
+  <class name="Symfony\Component\Form\Form">
+    <constraint name="Symfony\Component\Form\Extension\Validator\Constraints\Form" />
+    <property name="children">
+        <constraint name="Valid" />
+    </property>
+  </class>
+</constraint-mapping>
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 (file)
index 0000000..990b039
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>هذا النموذج يجب الا يحتوى على اى حقول اضافية.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>مساحة الملف المرسل كبيرة. من فضلك حاول ارسال ملف اصغر.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>قيمة رمز الموقع غير صحيحة. من فضلك اعد ارسال النموذج.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..6f00bde
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>\r
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">\r
+    <file source-language="en" datatype="plaintext" original="file.ext">\r
+        <body>\r
+            <trans-unit id="28">\r
+                <source>This form should not contain extra fields.</source>\r
+                <target>Тази форма не трябва да съдържа допълнителни полета.</target>\r
+            </trans-unit>\r
+            <trans-unit id="29">\r
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>\r
+                <target>Каченият файл е твърде голям. Моля, опитайте да качите по-малък файл.</target>\r
+            </trans-unit>\r
+            <trans-unit id="30">\r
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>\r
+                <target>Невалиден CSRF токен. Моля, опитайте да изпратите формата отново.</target>\r
+            </trans-unit>\r
+        </body>\r
+    </file>\r
+</xliff>
\ 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 (file)
index 0000000..3a2fa48
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Aquest formulari no hauria de contenir camps addicionals.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>L'arxiu pujat és massa gran. Per favor, pugi un arxiu més petit.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>El token CSRF no és vàlid. Per favor, provi d'enviar novament el formulari.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..776da49
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Tato skupina polí nesmí obsahovat další pole.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Nahraný soubor je příliš velký. Nahrajte prosím menší soubor.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>CSRF token je neplatný. Zkuste prosím znovu odeslat formulář.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..c2dd460
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Feltgruppen må ikke indeholde ekstra felter.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Den oploadede fil var for stor. Opload venligst en mindre fil.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>CSRF nøglen er ugyldig.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..c801d67
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Dieses Formular sollte keine zusätzlichen Felder enthalten.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Die hochgeladene Datei ist zu groß. Versuchen Sie bitte eine kleinere Datei hochzuladen.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>Das CSRF-Token ist ungültig. Versuchen Sie bitte das Formular erneut zu senden.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
\ 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 (file)
index 0000000..ba2ced7
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Αυτή η φόρμα δεν πρέπει να περιέχει επιπλέον πεδία.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Το αρχείο είναι πολύ μεγάλο. Παρακαλούμε προσπαθήστε να ανεβάσετε ένα μικρότερο αρχείο.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>Το CSRF token δεν είναι έγκυρο. Παρακαλούμε δοκιμάστε να υποβάλετε τη φόρμα ξανά.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..b8542d3
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>This form should not contain extra fields.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>The uploaded file was too large. Please try to upload a smaller file.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>The CSRF token is invalid. Please try to resubmit the form.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..ebfacfd
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Este formulario no debería contener campos adicionales.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>El archivo subido es demasiado grande. Por favor, suba un archivo más pequeño.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>El token CSRF no es válido. Por favor, pruebe de enviar nuevamente el formulario</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..1a9867f
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version='1.0'?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Väljade grupp ei tohiks sisalda lisaväljasid.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Üleslaaditud fail oli liiga suur. Palun proovi uuesti väiksema failiga.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>CSRF-märgis on vigane. Palun proovi vormi uuesti esitada.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..a07f786
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Formulario honek ez luke aparteko eremurik eduki behar.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Igotako fitxategia handiegia da. Mesedez saiatu fitxategi txikiago bat igotzen.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid.</source>
+                <target>CSFR tokena ez da egokia.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..468d2f6
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>این فرم نباید فیلد اضافی داشته باشد.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>فایل بارگذاری شده بسیار بزرگ است. لطفا فایل کوچکتری را بارگزاری کنید.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>مقدار CSRF نامعتبر است. لطفا فرم را مجددا ارسال فرمایید..</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..d223bb0
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This field group should not contain extra fields.</source>
+                <target>Tämä kenttäryhmä ei voi sisältää ylimääräisiä kenttiä.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Ladattu tiedosto on liian iso. Ole hyvä ja lataa pienempi tiedosto.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>CSRF tarkiste on virheellinen. Olen hyvä ja yritä lähettää lomake uudestaan.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..21f9010
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Ce formulaire ne doit pas contenir des champs supplémentaires.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Le fichier téléchargé est trop volumineux. Merci d'essayer d'envoyer un fichier plus petit.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>Le jeton CSRF est invalide. Veuillez renvoyer le formulaire.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..db23fe2
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Este formulario non debería conter campos adicionais.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>O arquivo subido é demasiado grande. Por favor, suba un arquivo máis pequeno.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>O token CSRF non é válido. Por favor, probe a enviar novamente o formulario</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..5198738
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>הטופס לא צריך להכיל שדות נוספים.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>הקובץ שהועלה גדול מדי. נסה להעלות קובץ קטן יותר.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid.</source>
+                <target>אסימון CSRF אינו חוקי.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..8d0bd29
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Ovaj obrazac ne smije sadržavati dodatna polja.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Prenesena datoteka je prevelika. Molim pokušajte prenijeti manju datoteku.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>CSRF vrijednost nije ispravna. Pokušajte ponovo poslati obrazac.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..d1491e7
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Ez a mezőcsoport nem tartalmazhat extra mezőket.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>A feltöltött fájl túl nagy. Kérem próbáljon egy kisebb fájlt feltölteni.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>Érvénytelen CSRF token. Kérem próbálja újra elküldeni az űrlapot.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..5a6091f
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Այս ձևը չպետք է պարունակի լրացուցիչ տողեր.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Վերբեռնված ֆայլը չափազանց մեծ է: Խնդրվում է վերբեռնել ավելի փոքր չափսի ֆայլ.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>CSRF արժեքը անթույլատրելի է: Փորձեք նորից ուղարկել ձևը.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
\ 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 (file)
index 0000000..b067d98
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Gabungan kolom tidak boleh mengandung kolom tambahan.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Berkas yang di unggah terlalu besar. Silahkan coba unggah berkas yang lebih kecil.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>CSRF-Token tidak sah. Silahkan coba kirim ulang formulir.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..aa15264
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Questo form non dovrebbe contenere nessun campo extra.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Il file caricato è troppo grande. Per favore caricare un file più piccolo.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>Il token CSRF non è valido. Provare a reinviare il form.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..4559af1
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>フィールドグループに追加のフィールドを含んではなりません.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>アップロードされたファイルが大きすぎます。小さなファイルで再度アップロードしてください.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid.</source>
+                <target>CSRFトークンが無効です.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..a111ffe
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Dës Feldergrupp sollt keng zousätzlech Felder enthalen.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>De geschécktene Fichier ass ze grouss. Versicht wann ech gelift ee méi klenge Fichier eropzelueden.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>Den CSRF-Token ass ongëlteg. Versicht wann ech gelift de Formulaire nach eng Kéier ze schécken.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
\ 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 (file)
index 0000000..25f3088
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Forma negali turėti papildomų laukų.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Įkelta byla yra per didelė. bandykite įkelti mažesnę.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>CSRF kodas nepriimtinas. Bandykite siųsti formos užklausą dar kartą.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
\ 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 (file)
index 0000000..9cdfb2c
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Šajā veidlapā nevajadzētu būt papildus ievades laukiem.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Augšupielādētā faila izmērs bija par lielu. Lūdzu mēģiniet augšupielādēt mazāka izmēra failu.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>Dotais CSRF talons nav derīgs. Lūdzu mēģiniet vēlreiz iesniegt veidlapu.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..002b01c
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Форм нэмэлт талбар багтаах боломжгүй.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Upload хийсэн файл хэтэрхий том байна. Бага хэмжээтэй файл оруулна уу.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>CSRF token буруу байна. Формоо дахин илгээнэ үү.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..5e36bd5
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Feltgruppen må ikke inneholde ekstra felter.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Den opplastede file var for stor. Vennligst last opp en mindre fil.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid.</source>
+                <target>CSRF nøkkelen er ugyldig.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..3d737d7
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Dit formulier mag geen extra velden bevatten.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Het geüploade bestand is te groot. Probeer een kleiner bestand te uploaden.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>De CSRF-token is ongeldig. Probeer het formulier opnieuw te versturen.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..64def2a
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Ten formularz nie powinien zawierać dodatkowych pól.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Wgrany plik był za duży. Proszę spróbować wgrać mniejszy plik.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>Token CSRF jest nieprawidłowy. Proszę spróbować wysłać formularz ponownie.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
\ 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 (file)
index 0000000..554a810
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Este formulário não deveria conter campos extra.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>O arquivo enviado é muito grande. Por favor, tente enviar um ficheiro mais pequeno.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>O token CSRF é inválido. Por favor submeta o formulário novamente.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..9ae4d71
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Este formulário não deve conter campos adicionais.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>O arquivo enviado é muito grande. Por favor, tente enviar um arquivo menor.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>O token CSRF é inválido. Por favor, tente reenviar o formulário.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..25abab3
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Aceast formular nu ar trebui să conțină câmpuri suplimentare.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Fișierul încărcat a fost prea mare. Vă rugăm sa încărcați un fișier mai mic.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>Token-ul CSRF este invalid. Vă rugăm să trimiteți formularul incă o dată.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
\ 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 (file)
index 0000000..8d829f1
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Эта форма не должна содержать дополнительных полей.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Загруженный файл слишком большой. Пожалуйста, попробуйте загрузить файл меньшего размера.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>CSRF значение недопустимо. Пожалуйста, попробуйте повторить отправку формы.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
\ 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 (file)
index 0000000..638d0cc
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Polia by nemali obsahovať ďalšie prvky.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Odoslaný súbor je príliš veľký. Prosím odošlite súbor s menšou veľkosťou.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>CSRF token je neplatný. Prosím skúste znovu odoslať formulár.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..103124d
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>To področje ne sme vsebovati dodatnih polj.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Naložena datoteka je prevelika. Prosim, poizkusite naložiti manjšo.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>CSRF vrednost je napačna. Prosimo, ponovno pošljite obrazec.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..ff7f550
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Овај формулар не треба да садржи додатна поља.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Отпремљена датотека је била превелика. Молим покушајте отпремање мање датотеке.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>CSRF вредност је невалидна. Покушајте поново.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
\ 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 (file)
index 0000000..6c4be35
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Ovaj formular ne treba da sadrži dodatna polja.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Otpremljena datoteka je bila prevelika. Molim pokušajte otpremanje manje datoteke.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>CSRF vrednost je nevalidna. Pokušajte ponovo.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
\ 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 (file)
index 0000000..4e2518b
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Formuläret kan inte innehålla extra fält.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Den uppladdade filen var för stor. Försök ladda upp en mindre fil.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid.</source>
+                <target>CSRF-symbolen är inte giltig.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..4c739fa
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>Ця форма не повинна містити додаткових полів.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>Завантажений файл занадто великий. Будь-ласка, спробуйте завантажити файл меншого розміру.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>CSRF значення недопустиме. Будь-ласка, спробуйте відправити форму знову.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
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 (file)
index 0000000..8bdf7fb
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="28">
+                <source>This form should not contain extra fields.</source>
+                <target>该表单中不可有额外字段.</target>
+            </trans-unit>
+            <trans-unit id="29">
+                <source>The uploaded file was too large. Please try to upload a smaller file.</source>
+                <target>上传文件太大, 请重新尝试上传一个较小的文件.</target>
+            </trans-unit>
+            <trans-unit id="30">
+                <source>The CSRF token is invalid. Please try to resubmit the form.</source>
+                <target>CSRF 验证符无效, 请重新提交.</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
diff --git a/vendor/symfony/form/Symfony/Component/Form/ReversedTransformer.php b/vendor/symfony/form/Symfony/Component/Form/ReversedTransformer.php
new file mode 100644 (file)
index 0000000..7069705
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+/**
+ * Reverses a transformer
+ *
+ * When the transform() method is called, the reversed transformer's
+ * reverseTransform() method is called and vice versa.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..47d4be0
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+/**
+ * A button that submits the form.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..088fb74
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+/**
+ * A builder for {@link SubmitButton} instances.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..f7ac13f
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+/**
+ * A type that should be converted into a {@link SubmitButton} instance.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..36417f7
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..711cecd
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..68e5f24
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Test;
+
+use Symfony\Component\Form\Forms;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..22a83fd
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..573f4e9
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Test;
+
+/**
+ * Base class for performance tests.
+ *
+ * Copied from Doctrine 2's OrmPerformanceTestCase.
+ *
+ * @author robo
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..9d51a9e
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * 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 (file)
index 0000000..ee9ed8f
--- /dev/null
@@ -0,0 +1,732 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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('<form>' . $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('</form>', $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 (file)
index 0000000..c16cb22
--- /dev/null
@@ -0,0 +1,43 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..2911818
--- /dev/null
@@ -0,0 +1,147 @@
+<?php
+
+    /*
+    * This file is part of the Symfony package.
+    *
+    * (c) Fabien Potencier <fabien@symfony.com>
+    *
+    * For the full copyright and license information, please view the LICENSE
+    * file that was distributed with this source code.
+    */
+
+namespace Symfony\Component\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 (file)
index 0000000..8f632a2
--- /dev/null
@@ -0,0 +1,1876 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <root> node so we can load HTML with multiple tags at
+            // the top level
+            $dom->loadXml('<root>'.$html.'</root>');
+        } 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 <root> and </root>
+                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('<form method="get" action="http://example.com/directory">', $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>',
+'/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('<form method="post" action="http://foo.com/directory">', $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('<form method="get" action="http://example.com/directory" enctype="multipart/form-data">', $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('<form method="get" action="http://example.com/directory" class="foobar">', $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 (file)
index 0000000..cef8f3b
--- /dev/null
@@ -0,0 +1,280 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..5c91195
--- /dev/null
@@ -0,0 +1,509 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <table>
+        // tag.
+        $this->assertMatchesXpath('<form>' . $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('</form>', $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 (file)
index 0000000..73c602c
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..b240d2d
--- /dev/null
@@ -0,0 +1,759 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..63eae9b
--- /dev/null
@@ -0,0 +1,200 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..bcd309e
--- /dev/null
@@ -0,0 +1,116 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..69c5aa0
--- /dev/null
@@ -0,0 +1,212 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..69d27a1
--- /dev/null
@@ -0,0 +1,188 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..ee2e335
--- /dev/null
@@ -0,0 +1,319 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..bafe5c0
--- /dev/null
@@ -0,0 +1,149 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * 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 (file)
index 0000000..41f8f95
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..bbae062
--- /dev/null
@@ -0,0 +1,76 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..5729719
--- /dev/null
@@ -0,0 +1,76 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..2ee2f22
--- /dev/null
@@ -0,0 +1,53 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..f7722c4
--- /dev/null
@@ -0,0 +1,20 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..4898b88
--- /dev/null
@@ -0,0 +1,512 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..cb50fc3
--- /dev/null
@@ -0,0 +1,275 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..98aeb77
--- /dev/null
@@ -0,0 +1,132 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..987df1d
--- /dev/null
@@ -0,0 +1,181 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..b54f0c4
--- /dev/null
@@ -0,0 +1,104 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..a90fa91
--- /dev/null
@@ -0,0 +1,115 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..8b91fe1
--- /dev/null
@@ -0,0 +1,74 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..c58e3f6
--- /dev/null
@@ -0,0 +1,393 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..104941c
--- /dev/null
@@ -0,0 +1,114 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..2c5298d
--- /dev/null
@@ -0,0 +1,120 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * 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 (file)
index 0000000..a5d5c78
--- /dev/null
@@ -0,0 +1,106 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..2b84e4f
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..b636f4b
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 (file)
index 0000000..6f46c9d
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..c0f3d59
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..5eb6c7b
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..dbd28c6
--- /dev/null
@@ -0,0 +1,259 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..1367b3e
--- /dev/null
@@ -0,0 +1,255 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..4e36893
--- /dev/null
@@ -0,0 +1,79 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..bfa1e21
--- /dev/null
@@ -0,0 +1,135 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests\Extension\Core\Type;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..55835e7
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests\Extension\Core\Type;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..c782ada
--- /dev/null
@@ -0,0 +1,162 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..0685946
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests\Extension\Core\Type;
+
+use Symfony\Component\Form\Test\FormPerformanceTestCase;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..219e818
--- /dev/null
@@ -0,0 +1,949 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..be3ad9d
--- /dev/null
@@ -0,0 +1,200 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..1d56e2a
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..b0eb6dc
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..b9c1eba
--- /dev/null
@@ -0,0 +1,477 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..2aa155e
--- /dev/null
@@ -0,0 +1,781 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..63556eb
--- /dev/null
@@ -0,0 +1,83 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..cced82f
--- /dev/null
@@ -0,0 +1,578 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..998bbe0
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..24434b2
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..e402cb4
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..97fc37f
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..e5b3dd7
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..bccb6f7
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..9e125d7
--- /dev/null
@@ -0,0 +1,149 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..8cc7228
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests\Extension\Core\Type;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..9bdfe15
--- /dev/null
@@ -0,0 +1,649 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..81df20c
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..733546e
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * 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 (file)
index 0000000..254b2a8
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..a99b544
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..1dcc6b4
--- /dev/null
@@ -0,0 +1,75 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..0bcfe74
--- /dev/null
@@ -0,0 +1,78 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..0a1f0dc
--- /dev/null
@@ -0,0 +1,301 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..2ff072b
--- /dev/null
@@ -0,0 +1,286 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..2d5cf77
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..a8bdde8
--- /dev/null
@@ -0,0 +1,748 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..528f946
--- /dev/null
@@ -0,0 +1,145 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..6619410
--- /dev/null
@@ -0,0 +1,85 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..d94d896
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * 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 (file)
index 0000000..7ad5b77
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..c802ea7
--- /dev/null
@@ -0,0 +1,1481 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..02df8f4
--- /dev/null
@@ -0,0 +1,245 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests\Extension\Validator\ViolationMapper;
+
+use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationPath;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..ee7d135
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+
+namespace Symfony\Component\Form\Tests\Fixtures;
+
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\FormEvents;
+use Symfony\Component\Form\FormEvent;
+use Symfony\Component\Form\FormBuilderInterface;
+
+class AlternatingRowType extends AbstractType
+{
+    public function buildForm(FormBuilderInterface $builder, array $options)
+    {
+        $formFactory = $builder->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 (file)
index 0000000..1120489
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..147f6e4
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+
+namespace Symfony\Component\Form\Tests\Fixtures;
+
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\OptionsResolver\OptionsResolverInterface;
+
+class AuthorType extends AbstractType
+{
+    public function buildForm(FormBuilderInterface $builder, array $options)
+    {
+        $builder
+            ->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 (file)
index 0000000..950f677
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..a5a3124
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..762a10b
--- /dev/null
@@ -0,0 +1,66 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..4f7ba6d
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..468b5a3
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..d26d3f7
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..c5f92e1
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..2e36475
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..f9de560
--- /dev/null
@@ -0,0 +1,72 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..e69de29
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 (file)
index 0000000..e076c97
--- /dev/null
@@ -0,0 +1,232 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..961dfd3
--- /dev/null
@@ -0,0 +1,148 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests;
+
+use Symfony\Component\Form\Exception\UnexpectedTypeException;
+use Symfony\Component\Form\FormConfigBuilder;
+use Symfony\Component\Form\Exception\InvalidArgumentException;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..a1292db
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..ea872b0
--- /dev/null
@@ -0,0 +1,506 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..763286c
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..39882e8
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..0c8bb6b
--- /dev/null
@@ -0,0 +1,243 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..69b048f
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..235eb6e
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..9d3a997
--- /dev/null
@@ -0,0 +1,219 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests;
+
+use Symfony\Component\Form\NativeRequestHandler;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..bb32a24
--- /dev/null
@@ -0,0 +1,280 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..bedad67
--- /dev/null
@@ -0,0 +1,1045 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 (file)
index 0000000..7647691
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Util;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..5c2c5fa
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..24fdc8b
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ *
+ * @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 (file)
index 0000000..7341501
--- /dev/null
@@ -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 (file)
index 0000000..d0d261f
--- /dev/null
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         processIsolation="false"
+         stopOnFailure="false"
+         syntaxCheck="false"
+         bootstrap="vendor/autoload.php"
+>
+    <testsuites>
+        <testsuite name="Symfony Form Component Test Suite">
+            <directory>./Tests/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./</directory>
+            <exclude>
+                <directory>./Tests</directory>
+                <directory>./vendor</directory>
+            </exclude>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/vendor/symfony/icu/Symfony/Component/Icu/.gitignore b/vendor/symfony/icu/Symfony/Component/Icu/.gitignore
new file mode 100644 (file)
index 0000000..c49a5d8
--- /dev/null
@@ -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 (file)
index 0000000..7c47e4e
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..478a2f0
--- /dev/null
@@ -0,0 +1,66 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Icu;
+
+use Symfony\Component\Intl\ResourceBundle\Reader\PhpBundleReader;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..a3de431
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..a9a5438
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..639a433
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..88a57f8
--- /dev/null
@@ -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 (file)
index 0000000..b0a16ed
--- /dev/null
@@ -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 (file)
index 0000000..3b38213
--- /dev/null
@@ -0,0 +1,1791 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+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 (file)
index 0000000..9d2ff14
--- /dev/null
@@ -0,0 +1,750 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+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 (file)
index 0000000..b1bcafc
--- /dev/null
@@ -0,0 +1,305 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+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 (file)
index 0000000..f42d657
--- /dev/null
@@ -0,0 +1,273 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+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 (file)
index 0000000..da31678
--- /dev/null
@@ -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 (file)
index 0000000..9fb65af
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..279430a
--- /dev/null
@@ -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 (file)
index 0000000..acfe4bd
--- /dev/null
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         processIsolation="false"
+         stopOnFailure="false"
+         syntaxCheck="false"
+         bootstrap="vendor/autoload.php"
+>
+    <testsuites>
+        <testsuite name="Symfony Icu Component Test Suite">
+            <directory>./Tests/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./</directory>
+            <exclude>
+                <directory>./Tests</directory>
+                <directory>./vendor</directory>
+            </exclude>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/.gitignore b/vendor/symfony/intl/Symfony/Component/Intl/.gitignore
new file mode 100644 (file)
index 0000000..c49a5d8
--- /dev/null
@@ -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 (file)
index 0000000..315c28a
--- /dev/null
@@ -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 (file)
index 0000000..8c0ffc3
--- /dev/null
@@ -0,0 +1,295 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <igor@wiedler.ch>
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..1a9601d
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\DateFormatter\DateFormat;
+
+/**
+ * Parser and formatter for AM/PM markers format
+ *
+ * @author Igor Wiedler <igor@wiedler.ch>
+ */
+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 (file)
index 0000000..ee53a4e
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\DateFormatter\DateFormat;
+
+/**
+ * Parser and formatter for day of week format
+ *
+ * @author Igor Wiedler <igor@wiedler.ch>
+ */
+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 (file)
index 0000000..2c33888
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\DateFormatter\DateFormat;
+
+/**
+ * Parser and formatter for day of year format
+ *
+ * @author Igor Wiedler <igor@wiedler.ch>
+ */
+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 (file)
index 0000000..19d30e7
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\DateFormatter\DateFormat;
+
+/**
+ * Parser and formatter for day format
+ *
+ * @author Igor Wiedler <igor@wiedler.ch>
+ */
+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 (file)
index 0000000..b89db36
--- /dev/null
@@ -0,0 +1,356 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <igor@wiedler.ch>
+ */
+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 (file)
index 0000000..8c8f5ef
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\DateFormatter\DateFormat;
+
+/**
+ * Parser and formatter for 12 hour format (0-11)
+ *
+ * @author Igor Wiedler <igor@wiedler.ch>
+ */
+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 (file)
index 0000000..a8c4370
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\DateFormatter\DateFormat;
+
+/**
+ * Parser and formatter for 12 hour format (1-12)
+ *
+ * @author Igor Wiedler <igor@wiedler.ch>
+ */
+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 (file)
index 0000000..8f22da1
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\DateFormatter\DateFormat;
+
+/**
+ * Parser and formatter for 24 hour format (0-23)
+ *
+ * @author Igor Wiedler <igor@wiedler.ch>
+ */
+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 (file)
index 0000000..b0f486b
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\DateFormatter\DateFormat;
+
+/**
+ * Parser and formatter for 24 hour format (1-24)
+ *
+ * @author Igor Wiedler <igor@wiedler.ch>
+ */
+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 (file)
index 0000000..51097d9
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\DateFormatter\DateFormat;
+
+/**
+ * Base class for hour transformers
+ *
+ * @author Eriksen Costa <eriksen.costa@infranology.com.br>
+ */
+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 (file)
index 0000000..b48de29
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\DateFormatter\DateFormat;
+
+/**
+ * Parser and formatter for minute format
+ *
+ * @author Igor Wiedler <igor@wiedler.ch>
+ */
+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 (file)
index 0000000..30c15af
--- /dev/null
@@ -0,0 +1,143 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\DateFormatter\DateFormat;
+
+/**
+ * Parser and formatter for month format
+ *
+ * @author Igor Wiedler <igor@wiedler.ch>
+ */
+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 (file)
index 0000000..8e83dc7
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\DateFormatter\DateFormat;
+
+/**
+ * Parser and formatter for quarter format
+ *
+ * @author Igor Wiedler <igor@wiedler.ch>
+ */
+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 (file)
index 0000000..ccbcdb4
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\DateFormatter\DateFormat;
+
+/**
+ * Parser and formatter for the second format
+ *
+ * @author Igor Wiedler <igor@wiedler.ch>
+ */
+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 (file)
index 0000000..7d74bd3
--- /dev/null
@@ -0,0 +1,99 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\DateFormatter\DateFormat;
+
+use Symfony\Component\Intl\Exception\NotImplementedException;
+
+/**
+ * Parser and formatter for time zone format
+ *
+ * @author Igor Wiedler <igor@wiedler.ch>
+ */
+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<signal>[+-])(?P<hours>\d{2}):?(?P<minutes>\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 (file)
index 0000000..0e67f8a
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\DateFormatter\DateFormat;
+
+/**
+ * Parser and formatter for date formats
+ *
+ * @author Igor Wiedler <igor@wiedler.ch>
+ */
+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 (file)
index 0000000..c3bd699
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\DateFormatter\DateFormat;
+
+/**
+ * Parser and formatter for year format
+ *
+ * @author Igor Wiedler <igor@wiedler.ch>
+ */
+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 (file)
index 0000000..33a499a
--- /dev/null
@@ -0,0 +1,631 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <igor@wiedler.ch>
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..ca79729
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Exception;
+
+/**
+ * Base BadMethodCallException for the Intl component.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..4fc076c
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Exception;
+
+/**
+ * Base ExceptionInterface for the Intl component.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..10f69ee
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Exception;
+
+/**
+ * InvalidArgumentException for the Intl component.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..570609d
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Exception;
+
+use Symfony\Component\Intl\Exception\NotImplementedException;
+
+/**
+ * @author Eriksen Costa <eriksen.costa@infranology.com.br>
+ */
+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 (file)
index 0000000..76e3f63
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Exception;
+
+use Symfony\Component\Intl\Exception\NotImplementedException;
+
+/**
+ * @author Eriksen Costa <eriksen.costa@infranology.com.br>
+ */
+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 (file)
index 0000000..d8a0e90
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Exception;
+
+/**
+ * @author Eriksen Costa <eriksen.costa@infranology.com.br>
+ */
+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 (file)
index 0000000..1f3ba46
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Exception;
+
+/**
+ * Base exception class for not implemented behaviors of the intl extension in the Locale component.
+ *
+ * @author Eriksen Costa <eriksen.costa@infranology.com.br>
+ */
+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 (file)
index 0000000..2727141
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Exception;
+
+/**
+ * Base OutOfBoundsException for the Intl component.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..9893714
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Exception;
+
+/**
+ * RuntimeException for the Intl component.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..6da0001
--- /dev/null
@@ -0,0 +1,137 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Globals;
+
+/**
+ * Provides fake static versions of the global functions in the intl extension
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..c13899a
--- /dev/null
@@ -0,0 +1,211 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..88a57f8
--- /dev/null
@@ -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 (file)
index 0000000..cca4e9e
--- /dev/null
@@ -0,0 +1,317 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <eriksen.costa@infranology.com.br>
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..0e5652e
--- /dev/null
@@ -0,0 +1,891 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <eriksen.costa@infranology.com.br>
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..ef4ba50
--- /dev/null
@@ -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 (file)
index 0000000..d1d523c
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle;
+
+use Symfony\Component\Intl\Intl;
+use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface;
+
+/**
+ * Base class for {@link ResourceBundleInterface} implementations.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..174aa17
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle\Compiler;
+
+use Symfony\Component\Intl\Exception\RuntimeException;
+
+/**
+ * Compiles .txt resource bundles to binary .res files.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..6184ea3
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle\Compiler;
+
+/**
+ * Compiles a resource bundle.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..6f2a0ed
--- /dev/null
@@ -0,0 +1,94 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle;
+
+/**
+ * Default implementation of {@link CurrencyBundleInterface}.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..1a88e93
--- /dev/null
@@ -0,0 +1,74 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle;
+
+/**
+ * Gives access to currency-related ICU data.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..c449f9c
--- /dev/null
@@ -0,0 +1,115 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle;
+
+/**
+ * Default implementation of {@link LanguageBundleInterface}.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..de50bda
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle;
+
+/**
+ * Gives access to language-related ICU data.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..6f6cdfc
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle;
+
+/**
+ * Default implementation of {@link LocaleBundleInterface}.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..daf5be6
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle;
+
+/**
+ * Gives access to locale-related ICU data.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..c30693a
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle\Reader;
+
+/**
+ * Base class for {@link BundleReaderInterface} implementations.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..56cef80
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle\Reader;
+
+use Symfony\Component\Intl\Exception\RuntimeException;
+use Symfony\Component\Intl\ResourceBundle\Util\ArrayAccessibleResourceBundle;
+
+/**
+ * Reads binary .res resource bundles.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..e44074b
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle\Reader;
+
+use Symfony\Component\Intl\ResourceBundle\Util\RingBuffer;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..bc485cd
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle\Reader;
+
+/**
+ * Reads resource bundle files.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..663bcc9
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle\Reader;
+
+use Symfony\Component\Intl\Exception\InvalidArgumentException;
+use Symfony\Component\Intl\Exception\RuntimeException;
+
+/**
+ * Reads .php resource bundles.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..e3656fe
--- /dev/null
@@ -0,0 +1,113 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle\Reader;
+
+use Symfony\Component\Intl\ResourceBundle\Util\RecursiveArrayAccess;
+
+/**
+ * A structured reader wrapping an existing resource bundle reader.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ *
+ * @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 (file)
index 0000000..c22ad93
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle\Reader;
+
+/**
+ * Reads individual entries of a resource file.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..a3cd9bd
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle;
+
+/**
+ * Default implementation of {@link RegionBundleInterface}.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..4e55f2d
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle;
+
+/**
+ * Gives access to region-related ICU data.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..497a66a
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle;
+
+/**
+ * Gives access to ICU data.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..0692d6f
--- /dev/null
@@ -0,0 +1,96 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..cdc1951
--- /dev/null
@@ -0,0 +1,97 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle\Transformer;
+
+use Symfony\Component\Filesystem\Filesystem;
+use Symfony\Component\Intl\ResourceBundle\Compiler\BundleCompilerInterface;
+
+/**
+ * Default implementation of {@link CompilationContextInterface}.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..f05c280
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle\Transformer;
+
+/**
+ * Stores contextual information for resource bundle compilation.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..95783b3
--- /dev/null
@@ -0,0 +1,94 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..5e6f901
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..b2576d6
--- /dev/null
@@ -0,0 +1,251 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..52fdbed
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..3965e0d
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..25ab68d
--- /dev/null
@@ -0,0 +1,80 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle\Transformer;
+
+use Symfony\Component\Filesystem\Filesystem;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..dc49255
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle\Transformer;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..9a4cccb
--- /dev/null
@@ -0,0 +1,79 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..e1feaa2
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle\Util;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..7ccbd1e
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\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 <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..cc3b958
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle\Writer;
+
+/**
+ * Writes resource bundle files.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+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 (file)
index 0000000..d2688b4
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle\Writer;
+
+/**
+ * Writes .php resource bundles.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class PhpBundleWriter implements BundleWriterInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function write($path, $locale, $data)
+    {
+        $template = <<<TEMPLATE
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return %s;
+
+TEMPLATE;
+
+        $data = var_export($data, true);
+        $data = preg_replace('/array \(/', 'array(', $data);
+        $data = preg_replace('/\n {1,10}array\(/', 'array(', $data);
+        $data = preg_replace('/  /', '    ', $data);
+        $data = sprintf($template, $data);
+
+        file_put_contents($path.'/'.$locale.'.php', $data);
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Writer/TextBundleWriter.php b/vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Writer/TextBundleWriter.php
new file mode 100644 (file)
index 0000000..342ee2d
--- /dev/null
@@ -0,0 +1,202 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\ResourceBundle\Writer;
+
+/**
+ * Writes .txt resource bundles.
+ *
+ * The resulting files can be converted to binary .res files using the
+ * {@link \Symfony\Component\Intl\ResourceBundle\Transformer\BundleCompiler}.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ *
+ * @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
+ */
+class TextBundleWriter implements BundleWriterInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function write($path, $locale, $data)
+    {
+        $file = fopen($path.'/'.$locale.'.txt', 'w');
+
+        $this->writeResourceBundle($file, $locale, $data);
+
+        fclose($file);
+    }
+
+    /**
+     * Writes a "resourceBundle" node.
+     *
+     * @param resource $file       The file handle to write to.
+     * @param string   $bundleName The name of the bundle.
+     * @param mixed    $value      The value of the node.
+     *
+     * @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
+     */
+    private function writeResourceBundle($file, $bundleName, $value)
+    {
+        fwrite($file, $bundleName);
+
+        $this->writeTable($file, $value, 0);
+
+        fwrite($file, "\n");
+    }
+
+    /**
+     * Writes a "resource" node.
+     *
+     * @param resource $file          The file handle to write to.
+     * @param mixed    $value         The value of the node.
+     * @param integer  $indentation   The number of levels to indent.
+     * @param Boolean  $requireBraces Whether to require braces to be printed
+     *                                around the value.
+     *
+     * @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
+     */
+    private function writeResource($file, $value, $indentation, $requireBraces = true)
+    {
+        if (is_int($value)) {
+            $this->writeInteger($file, $value);
+
+            return;
+        }
+
+        if (is_array($value)) {
+            if (count($value) === count(array_filter($value, 'is_int'))) {
+                $this->writeIntVector($file, $value, $indentation);
+
+                return;
+            }
+
+            $keys = array_keys($value);
+
+            if (count($keys) === count(array_filter($keys, 'is_int'))) {
+                $this->writeArray($file, $value, $indentation);
+
+                return;
+            }
+
+            $this->writeTable($file, $value, $indentation);
+
+            return;
+        }
+
+        if (is_bool($value)) {
+            $value = $value ? 'true' : 'false';
+        }
+
+        $this->writeString($file, (string) $value, $requireBraces);
+    }
+
+    /**
+     * Writes an "integer" node.
+     *
+     * @param resource $file  The file handle to write to.
+     * @param integer  $value The value of the node.
+     *
+     * @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
+     */
+    private function writeInteger($file, $value)
+    {
+        fprintf($file, ':int{%d}', $value);
+    }
+
+    /**
+     * Writes an "intvector" node.
+     *
+     * @param resource $file        The file handle to write to.
+     * @param array    $value       The value of the node.
+     * @param integer  $indentation The number of levels to indent.
+     *
+     * @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
+     */
+    private function writeIntVector($file, array $value, $indentation)
+    {
+        fwrite($file, ":intvector{\n");
+
+        foreach ($value as $int) {
+            fprintf($file, "%s%d,\n", str_repeat('    ', $indentation + 1), $int);
+        }
+
+        fprintf($file, "%s}", str_repeat('    ', $indentation));
+    }
+
+    /**
+     * Writes a "string" node.
+     *
+     * @param resource $file         The file handle to write to.
+     * @param string   $value        The value of the node.
+     * @param Boolean  $requireBraces Whether to require braces to be printed
+     *                                around the value.
+     *
+     * @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
+     */
+    private function writeString($file, $value, $requireBraces = true)
+    {
+        if ($requireBraces) {
+            fprintf($file, '{"%s"}', $value);
+
+            return;
+        }
+
+        fprintf($file, '"%s"', $value);
+    }
+
+    /**
+     * Writes an "array" node.
+     *
+     * @param resource $file        The file handle to write to.
+     * @param array    $value       The value of the node.
+     * @param integer  $indentation The number of levels to indent.
+     *
+     * @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
+     */
+    private function writeArray($file, array $value, $indentation)
+    {
+        fwrite($file, "{\n");
+
+        foreach ($value as $entry) {
+            fwrite($file, str_repeat('    ', $indentation + 1));
+
+            $this->writeResource($file, $entry, $indentation + 1, false);
+
+            fwrite($file, ",\n");
+        }
+
+        fprintf($file, '%s}', str_repeat('    ', $indentation));
+    }
+
+    /**
+     * Writes a "table" node.
+     *
+     * @param resource $file        The file handle to write to.
+     * @param array    $value       The value of the node.
+     * @param integer  $indentation The number of levels to indent.
+     */
+    private function writeTable($file, array $value, $indentation)
+    {
+        fwrite($file, "{\n");
+
+        foreach ($value as $key => $entry) {
+            fwrite($file, str_repeat('    ', $indentation + 1));
+            fwrite($file, $key);
+
+            $this->writeResource($file, $entry, $indentation + 1);
+
+            fwrite($file, "\n");
+        }
+
+        fprintf($file, '%s}', str_repeat('    ', $indentation));
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/autoload.php b/vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/autoload.php
new file mode 100644 (file)
index 0000000..e450011
--- /dev/null
@@ -0,0 +1,18 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+$autoload = __DIR__ . '/../../vendor/autoload.php';
+
+if (!file_exists($autoload)) {
+    bailout('You should run "composer install --dev" in the component before running this script.');
+}
+
+require_once realpath($autoload);
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/common.php b/vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/common.php
new file mode 100644 (file)
index 0000000..4fadbe8
--- /dev/null
@@ -0,0 +1,69 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+define('LINE_WIDTH', 75);
+
+define('LINE', str_repeat('-', LINE_WIDTH) . "\n");
+
+function bailout($message)
+{
+    echo wordwrap($message, LINE_WIDTH) . " Aborting.\n";
+
+    exit(1);
+}
+
+function strip_minor_versions($version)
+{
+    preg_match('/^(?P<version>[0-9]\.[0-9]|[0-9]{2,})/', $version, $matches);
+
+    return $matches['version'];
+}
+
+function centered($text)
+{
+    $padding = (int) ((LINE_WIDTH - strlen($text))/2);
+
+    return str_repeat(' ', $padding) . $text;
+}
+
+function cd($dir)
+{
+    if (false === chdir($dir)) {
+        bailout("Could not switch to directory $dir.");
+    }
+}
+
+function run($command)
+{
+    exec($command, $output, $status);
+
+    if (0 !== $status) {
+        $output = implode("\n", $output);
+        echo "Error while running:\n    " . getcwd() . '$ ' . $command . "\nOutput:\n" . LINE . "$output\n" . LINE;
+
+        bailout("\"$command\" failed.");
+    }
+}
+
+function get_icu_version_from_genrb($genrb)
+{
+    exec($genrb . ' --version 2>&1', $output, $status);
+
+    if (0 !== $status) {
+        bailout($genrb . ' failed.');
+    }
+
+    if (!preg_match('/ICU version ([\d\.]+)/', implode('', $output), $matches)) {
+        return null;
+    }
+
+    return $matches[1];
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/copy-stubs-to-component.php b/vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/copy-stubs-to-component.php
new file mode 100644 (file)
index 0000000..e857683
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Symfony\Component\Filesystem\Filesystem;
+use Symfony\Component\Icu\IcuData;
+use Symfony\Component\Intl\Intl;
+
+require_once __DIR__ . '/common.php';
+require_once __DIR__ . '/autoload.php';
+
+if (1 !== $GLOBALS['argc']) {
+    bailout(<<<MESSAGE
+Usage: php copy-stubs-to-component.php
+
+Copies stub files created with create-stubs.php to the Icu component.
+
+For running this script, the intl extension must be loaded and all vendors
+must have been installed through composer:
+
+    composer install --dev
+
+MESSAGE
+    );
+}
+
+echo LINE;
+echo centered("ICU Resource Bundle Stub Update") . "\n";
+echo LINE;
+
+if (!class_exists('\Symfony\Component\Icu\IcuData')) {
+    bailout('You must run "composer update --dev" before running this script.');
+}
+
+$stubBranch = '1.0.x';
+
+if (!IcuData::isStubbed()) {
+    bailout("Please switch to the Icu component branch $stubBranch.");
+}
+
+$filesystem = new Filesystem();
+
+$sourceDir = sys_get_temp_dir() . '/icu-stubs';
+$targetDir = IcuData::getResourceDirectory();
+
+if (!$filesystem->exists($sourceDir)) {
+    bailout("The directory $sourceDir does not exist. Please run create-stubs.php first.");
+}
+
+$filesystem->remove($targetDir);
+
+echo "Copying files from $sourceDir to $targetDir...\n";
+
+$filesystem->mirror($sourceDir, $targetDir);
+
+echo "Done.\n";
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/create-stubs.php b/vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/create-stubs.php
new file mode 100644 (file)
index 0000000..d330d6b
--- /dev/null
@@ -0,0 +1,112 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Symfony\Component\Filesystem\Filesystem;
+use Symfony\Component\Icu\IcuData;
+use Symfony\Component\Intl\Intl;
+use Symfony\Component\Intl\ResourceBundle\Transformer\BundleTransformer;
+use Symfony\Component\Intl\ResourceBundle\Transformer\Rule\CurrencyBundleTransformationRule;
+use Symfony\Component\Intl\ResourceBundle\Transformer\Rule\LanguageBundleTransformationRule;
+use Symfony\Component\Intl\ResourceBundle\Transformer\Rule\LocaleBundleTransformationRule;
+use Symfony\Component\Intl\ResourceBundle\Transformer\Rule\RegionBundleTransformationRule;
+use Symfony\Component\Intl\ResourceBundle\Transformer\StubbingContext;
+
+require_once __DIR__ . '/common.php';
+require_once __DIR__ . '/autoload.php';
+
+if (1 !== $GLOBALS['argc']) {
+    bailout(<<<MESSAGE
+Usage: php create-stubs.php
+
+Creates resource bundle stubs from the resource bundles in the Icu component.
+
+For running this script, the intl extension must be loaded and all vendors
+must have been installed through composer:
+
+    composer install --dev
+
+MESSAGE
+    );
+}
+
+echo LINE;
+echo centered("ICU Resource Bundle Stub Creation") . "\n";
+echo LINE;
+
+if (!Intl::isExtensionLoaded()) {
+    bailout('The intl extension for PHP is not installed.');
+}
+
+if (!class_exists('\Symfony\Component\Icu\IcuData')) {
+    bailout('You must run "composer update --dev" before running this script.');
+}
+
+$stubBranch = '1.0.x';
+
+if (IcuData::isStubbed()) {
+    bailout("Please switch to a branch of the Icu component that contains .res files (anything but $stubBranch).");
+}
+
+$shortIcuVersionInPhp = strip_minor_versions(Intl::getIcuVersion());
+$shortIcuVersionInIntlComponent = strip_minor_versions(Intl::getIcuStubVersion());
+$shortIcuVersionInIcuComponent = strip_minor_versions(IcuData::getVersion());
+
+if ($shortIcuVersionInPhp !== $shortIcuVersionInIcuComponent) {
+    bailout("The ICU version of the component ($shortIcuVersionInIcuComponent) does not match the ICU version in the intl extension ($shortIcuVersionInPhp).");
+}
+
+if ($shortIcuVersionInIntlComponent !== $shortIcuVersionInIcuComponent) {
+    bailout("The ICU version of the component ($shortIcuVersionInIcuComponent) does not match the ICU version of the stub classes in the Intl component ($shortIcuVersionInIntlComponent).");
+}
+
+echo wordwrap("Make sure that you don't have any ICU development files " .
+    "installed. If the build fails, try to run:\n", LINE_WIDTH);
+
+echo "\n    sudo apt-get remove libicu-dev\n\n";
+
+$icuVersionInIcuComponent = IcuData::getVersion();
+
+echo "Compiling stubs for ICU version $icuVersionInIcuComponent.\n";
+
+echo "Preparing stub creation...\n";
+
+$targetDir = sys_get_temp_dir() . '/icu-stubs';
+
+$context = new StubbingContext(
+    IcuData::getResourceDirectory(),
+    $targetDir,
+    new Filesystem(),
+    $icuVersionInIcuComponent
+);
+
+$transformer = new BundleTransformer();
+$transformer->addRule(new LanguageBundleTransformationRule());
+$transformer->addRule(new RegionBundleTransformationRule());
+$transformer->addRule(new CurrencyBundleTransformationRule());
+$transformer->addRule(new LocaleBundleTransformationRule());
+
+echo "Starting stub creation...\n";
+
+$transformer->createStubs($context);
+
+echo "Wrote stubs to $targetDir.\n";
+
+$versionFile = $context->getStubDir() . '/version.txt';
+
+file_put_contents($versionFile, "$icuVersionInIcuComponent\n");
+
+echo "Wrote $versionFile.\n";
+
+echo "Done.\n";
+
+echo wordwrap("Please change the Icu component to branch $stubBranch now and run:\n", LINE_WIDTH);
+
+echo "\n    php copy-stubs-to-component.php\n";
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/icu-version.php b/vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/icu-version.php
new file mode 100644 (file)
index 0000000..d54916f
--- /dev/null
@@ -0,0 +1,18 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Symfony\Component\Intl\Intl;
+
+require_once __DIR__ . '/common.php';
+require_once __DIR__ . '/autoload.php';
+
+echo "ICU version: ";
+echo Intl::getIcuVersion() . "\n";
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/icu.ini b/vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/icu.ini
new file mode 100644 (file)
index 0000000..902e336
--- /dev/null
@@ -0,0 +1,9 @@
+; ICU data source URLs
+; We use always the latest release of a major version.
+4.0 = http://source.icu-project.org/repos/icu/icu/tags/release-4-0-1/source
+4.2 = http://source.icu-project.org/repos/icu/icu/tags/release-4-2-1/source
+4.4 = http://source.icu-project.org/repos/icu/icu/tags/release-4-4-2/source
+4.6 = http://source.icu-project.org/repos/icu/icu/tags/release-4-6-1/source
+4.8 = http://source.icu-project.org/repos/icu/icu/tags/release-4-8-1-1/source
+49 = http://source.icu-project.org/repos/icu/icu/tags/release-49-1-2/source
+50 = http://source.icu-project.org/repos/icu/icu/tags/release-50-1-2/source
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/test-compat.php b/vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/test-compat.php
new file mode 100644 (file)
index 0000000..c1bf40f
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Symfony\Component\Intl\Intl;
+
+require_once __DIR__ . '/common.php';
+require_once __DIR__ . '/autoload.php';
+
+if (1 !== $GLOBALS['argc']) {
+    bailout(<<<MESSAGE
+Usage: php test-compat.php
+
+Tests the compatibility of the current ICU version (bundled in ext/intl) with
+different versions of symfony/icu.
+
+For running this script, the intl extension must be loaded and all vendors
+must have been installed through composer:
+
+    composer install --dev
+
+MESSAGE
+    );
+}
+
+echo LINE;
+echo centered("ICU Compatibility Test") . "\n";
+echo LINE;
+
+echo "Your ICU version: " . Intl::getIcuVersion() . "\n";
+
+echo "Compatibility with symfony/icu:\n";
+
+$branches = array(
+    '1.1.x',
+    '1.2.x',
+);
+
+cd(__DIR__ . '/../../vendor/symfony/icu/Symfony/Component/Icu');
+
+foreach ($branches as $branch) {
+    run('git checkout ' . $branch . ' 2>&1');
+
+    exec('php ' . __DIR__ . '/util/test-compat-helper.php > /dev/null 2> /dev/null', $output, $status);
+
+    echo "$branch: " . (0 === $status ? "YES" : "NO") . "\n";
+}
+
+echo "Done.\n";
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/update-icu-component.php b/vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/update-icu-component.php
new file mode 100644 (file)
index 0000000..2b94fe4
--- /dev/null
@@ -0,0 +1,212 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Symfony\Component\Icu\IcuData;
+use Symfony\Component\Intl\Intl;
+use Symfony\Component\Intl\ResourceBundle\Compiler\BundleCompiler;
+use Symfony\Component\Intl\ResourceBundle\Transformer\BundleTransformer;
+use Symfony\Component\Intl\ResourceBundle\Transformer\CompilationContext;
+use Symfony\Component\Intl\ResourceBundle\Transformer\Rule\CurrencyBundleTransformationRule;
+use Symfony\Component\Intl\ResourceBundle\Transformer\Rule\LanguageBundleTransformationRule;
+use Symfony\Component\Intl\ResourceBundle\Transformer\Rule\LocaleBundleTransformationRule;
+use Symfony\Component\Intl\ResourceBundle\Transformer\Rule\RegionBundleTransformationRule;
+use Symfony\Component\Intl\Util\SvnRepository;
+use Symfony\Component\Filesystem\Filesystem;
+
+require_once __DIR__ . '/common.php';
+require_once __DIR__ . '/autoload.php';
+
+if ($GLOBALS['argc'] > 3 || 2 === $GLOBALS['argc'] && '-h' === $GLOBALS['argv'][1]) {
+    bailout(<<<MESSAGE
+Usage: php update-icu-component.php <path/to/icu/source> <path/to/icu/build>
+
+Updates the ICU data for Symfony2 to the latest version of the ICU version
+included in the intl extension. For example, if your intl extension includes
+ICU 4.8, the script will download the latest data available for ICU 4.8.
+
+If you downloaded the SVN repository before, you can pass the path to the
+repository source in the first optional argument.
+
+If you also built the repository before, you can pass the directory where that
+build is stored in the second parameter. The build directory needs to contain
+the subdirectories bin/ and lib/.
+
+For running this script, the intl extension must be loaded and all vendors
+must have been installed through composer:
+
+    composer install --dev
+
+MESSAGE
+    );
+}
+
+echo LINE;
+echo centered("ICU Resource Bundle Compilation") . "\n";
+echo LINE;
+
+if (!Intl::isExtensionLoaded()) {
+    bailout('The intl extension for PHP is not installed.');
+}
+
+if (!class_exists('\Symfony\Component\Icu\IcuData')) {
+    bailout('You must run "composer update --dev" before running this script.');
+}
+
+$filesystem = new Filesystem();
+
+$icuVersionInPhp = Intl::getIcuVersion();
+
+echo "Found intl extension with ICU version $icuVersionInPhp.\n";
+
+$shortIcuVersion = strip_minor_versions($icuVersionInPhp);
+$urls = parse_ini_file(__DIR__ . '/icu.ini');
+
+if (!isset($urls[$shortIcuVersion])) {
+    bailout('The version ' . $shortIcuVersion . ' is not available in the icu.ini file.');
+}
+
+echo "icu.ini parsed. Available versions:\n";
+
+foreach ($urls as $urlVersion => $url) {
+    echo "  $urlVersion\n";
+}
+
+if ($GLOBALS['argc'] >= 2) {
+    $sourceDir = $GLOBALS['argv'][1];
+    $svn = new SvnRepository($sourceDir);
+
+    echo "Using existing SVN repository at {$sourceDir}.\n";
+} else {
+    echo "Starting SVN checkout for version $shortIcuVersion. This may take a while...\n";
+
+    $sourceDir = sys_get_temp_dir() . '/icu-data/' . $shortIcuVersion . '/source';
+    $svn = SvnRepository::download($urls[$shortIcuVersion], $sourceDir);
+
+    echo "SVN checkout to {$sourceDir} complete.\n";
+}
+
+if ($GLOBALS['argc'] >= 3) {
+    $buildDir = $GLOBALS['argv'][2];
+} else {
+    // Always build genrb so that we can determine the ICU version of the
+    // download by running genrb --version
+    echo "Building genrb.\n";
+
+    cd($sourceDir);
+
+    echo "Running configure...\n";
+
+    $buildDir = sys_get_temp_dir() . '/icu-data/' . $shortIcuVersion . '/build';
+
+    $filesystem->remove($buildDir);
+    $filesystem->mkdir($buildDir);
+
+    run('./configure --prefix=' . $buildDir . ' 2>&1');
+
+    echo "Running make...\n";
+
+    // If the directory "lib" does not exist in the download, create it or we
+    // will run into problems when building libicuuc.so.
+    $filesystem->mkdir($sourceDir . '/lib');
+
+    // If the directory "bin" does not exist in the download, create it or we
+    // will run into problems when building genrb.
+    $filesystem->mkdir($sourceDir . '/bin');
+
+    echo "[1/5] libicudata.so...";
+
+    cd($sourceDir . '/stubdata');
+    run('make 2>&1 && make install 2>&1');
+
+    echo " ok.\n";
+
+    echo "[2/5] libicuuc.so...";
+
+    cd($sourceDir . '/common');
+    run('make 2>&1 && make install 2>&1');
+
+    echo " ok.\n";
+
+    echo "[3/5] libicui18n.so...";
+
+    cd($sourceDir . '/i18n');
+    run('make 2>&1 && make install 2>&1');
+
+    echo " ok.\n";
+
+    echo "[4/5] libicutu.so...";
+
+    cd($sourceDir . '/tools/toolutil');
+    run('make 2>&1 && make install 2>&1');
+
+    echo " ok.\n";
+
+    echo "[5/5] genrb...";
+
+    cd($sourceDir . '/tools/genrb');
+    run('make 2>&1 && make install 2>&1');
+
+    echo " ok.\n";
+}
+
+$genrb = $buildDir . '/bin/genrb';
+$genrbEnv = 'LD_LIBRARY_PATH=' . $buildDir . '/lib ';
+
+echo "Using $genrb.\n";
+
+$icuVersionInDownload = get_icu_version_from_genrb($genrbEnv . ' ' . $genrb);
+
+echo "Preparing resource bundle compilation (version $icuVersionInDownload)...\n";
+
+$context = new CompilationContext(
+    $sourceDir . '/data',
+    IcuData::getResourceDirectory(),
+    $filesystem,
+    new BundleCompiler($genrb, $genrbEnv),
+    $icuVersionInDownload
+);
+
+$transformer = new BundleTransformer();
+$transformer->addRule(new LanguageBundleTransformationRule());
+$transformer->addRule(new RegionBundleTransformationRule());
+$transformer->addRule(new CurrencyBundleTransformationRule());
+$transformer->addRule(new LocaleBundleTransformationRule());
+
+echo "Starting resource bundle compilation. This may take a while...\n";
+
+$transformer->compileBundles($context);
+
+echo "Resource bundle compilation complete.\n";
+
+$svnInfo = <<<SVN_INFO
+SVN information
+===============
+
+URL: {$svn->getUrl()}
+Revision: {$svn->getLastCommit()->getRevision()}
+Author: {$svn->getLastCommit()->getAuthor()}
+Date: {$svn->getLastCommit()->getDate()}
+
+SVN_INFO;
+
+$svnInfoFile = $context->getBinaryDir() . '/svn-info.txt';
+
+file_put_contents($svnInfoFile, $svnInfo);
+
+echo "Wrote $svnInfoFile.\n";
+
+$versionFile = $context->getBinaryDir() . '/version.txt';
+
+file_put_contents($versionFile, "$icuVersionInDownload\n");
+
+echo "Wrote $versionFile.\n";
+
+echo "Done.\n";
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/util/test-compat-helper.php b/vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/util/test-compat-helper.php
new file mode 100644 (file)
index 0000000..2734895
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Symfony\Component\Icu\IcuData;
+use Symfony\Component\Intl\ResourceBundle\Reader\BinaryBundleReader;
+
+require_once __DIR__ . '/../common.php';
+require_once __DIR__ . '/../autoload.php';
+
+$reader = new BinaryBundleReader();
+
+$reader->read(IcuData::getResourceDirectory() . '/curr', 'en');
+$reader->read(IcuData::getResourceDirectory() . '/lang', 'en');
+$reader->read(IcuData::getResourceDirectory() . '/locales', 'en');
+$reader->read(IcuData::getResourceDirectory() . '/region', 'en');
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Resources/stubs/Collator.php b/vendor/symfony/intl/Symfony/Component/Intl/Resources/stubs/Collator.php
new file mode 100644 (file)
index 0000000..4c373d8
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Stub implementation for the Collator class of the intl extension
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ *
+ * @see \Symfony\Component\Intl\Collator\StubCollator
+ */
+class Collator extends \Symfony\Component\Intl\Collator\Collator
+{
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Resources/stubs/IntlDateFormatter.php b/vendor/symfony/intl/Symfony/Component/Intl/Resources/stubs/IntlDateFormatter.php
new file mode 100644 (file)
index 0000000..52a07e9
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Stub implementation for the IntlDateFormatter class of the intl extension
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ *
+ * @see \Symfony\Component\Intl\DateFormatter\IntlDateFormatter
+ */
+class IntlDateFormatter extends \Symfony\Component\Intl\DateFormatter\IntlDateFormatter
+{
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Resources/stubs/Locale.php b/vendor/symfony/intl/Symfony/Component/Intl/Resources/stubs/Locale.php
new file mode 100644 (file)
index 0000000..045db40
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Stub implementation for the Locale class of the intl extension
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ *
+ * @see \Symfony\Component\Intl\Locale\Locale
+ */
+class Locale extends \Symfony\Component\Intl\Locale\Locale
+{
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Resources/stubs/NumberFormatter.php b/vendor/symfony/intl/Symfony/Component/Intl/Resources/stubs/NumberFormatter.php
new file mode 100644 (file)
index 0000000..318efb4
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Stub implementation for the NumberFormatter class of the intl extension
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ *
+ * @see \Symfony\Component\Intl\NumberFormatter\NumberFormatter
+ */
+class NumberFormatter extends \Symfony\Component\Intl\NumberFormatter\NumberFormatter
+{
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Resources/stubs/functions.php b/vendor/symfony/intl/Symfony/Component/Intl/Resources/stubs/functions.php
new file mode 100644 (file)
index 0000000..7a2d4b6
--- /dev/null
@@ -0,0 +1,80 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Symfony\Component\Intl\Globals\IntlGlobals;
+
+if (!function_exists('intl_is_failure')) {
+
+    /**
+     * Stub implementation for the {@link intl_is_failure()} function of the intl
+     * extension.
+     *
+     * @author Bernhard Schussek <bschussek@gmail.com>
+     *
+     * @param integer $errorCode  The error code returned by intl_get_error_code().
+     *
+     * @return Boolean Whether the error code indicates an error.
+     *
+     * @see \Symfony\Component\Intl\Globals\StubIntlGlobals::isFailure
+     */
+    function intl_is_failure($errorCode)
+    {
+        return IntlGlobals::isFailure($errorCode);
+    }
+
+    /**
+     * Stub implementation for the {@link intl_get_error_code()} function of the
+     * intl extension.
+     *
+     * @author Bernhard Schussek <bschussek@gmail.com>
+     *
+     * @return Boolean The error code of the last intl function call or
+     *                 IntlGlobals::U_ZERO_ERROR if no error occurred.
+     *
+     * @see \Symfony\Component\Intl\Globals\StubIntlGlobals::getErrorCode
+     */
+    function intl_get_error_code()
+    {
+        return IntlGlobals::getErrorCode();
+    }
+
+    /**
+     * Stub implementation for the {@link intl_get_error_code()} function of the
+     * intl extension.
+     *
+     * @author Bernhard Schussek <bschussek@gmail.com>
+     *
+     * @return Boolean The error message of the last intl function call or
+     *                 "U_ZERO_ERROR" if no error occurred.
+     *
+     * @see \Symfony\Component\Intl\Globals\StubIntlGlobals::getErrorMessage
+     */
+    function intl_get_error_message()
+    {
+        return IntlGlobals::getErrorMessage();
+    }
+
+    /**
+     * Stub implementation for the {@link intl_error_name()} function of the intl
+     * extension.
+     *
+     * @param integer $errorCode The error code.
+     *
+     * @return string The name of the error code constant.
+     *
+     * @see \Symfony\Component\Intl\Globals\StubIntlGlobals::getErrorName
+     */
+    function intl_error_name($errorCode)
+    {
+        return IntlGlobals::getErrorName($errorCode);
+    }
+
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/Collator/AbstractCollatorTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/Collator/AbstractCollatorTest.php
new file mode 100644 (file)
index 0000000..08f3a56
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\Collator;
+
+use Symfony\Component\Intl\Collator\Collator;
+use Symfony\Component\Intl\Locale;
+
+/**
+ * Test case for Collator implementations.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+abstract class AbstractCollatorTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @dataProvider asortProvider
+     */
+    public function testAsort($array, $sortFlag, $expected)
+    {
+        $collator = $this->getCollator('en');
+        $collator->asort($array, $sortFlag);
+        $this->assertSame($expected, $array);
+    }
+
+    public function asortProvider()
+    {
+        return array(
+            /* array, sortFlag, expected */
+            array(
+                array('a', 'b', 'c'),
+                Collator::SORT_REGULAR,
+                array('a', 'b', 'c'),
+            ),
+            array(
+                array('c', 'b', 'a'),
+                Collator::SORT_REGULAR,
+                array(2 => 'a', 1 => 'b',  0 => 'c'),
+            ),
+            array(
+                array('b', 'c', 'a'),
+                Collator::SORT_REGULAR,
+                array(2 => 'a', 0 => 'b', 1 => 'c'),
+            ),
+        );
+    }
+
+    /**
+     * @param string $locale
+     *
+     * @return \Collator
+     */
+    abstract protected function getCollator($locale);
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/Collator/CollatorTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/Collator/CollatorTest.php
new file mode 100644 (file)
index 0000000..a4e4e56
--- /dev/null
@@ -0,0 +1,109 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\Collator;
+
+use Symfony\Component\Intl\Collator\Collator;
+use Symfony\Component\Intl\Globals\IntlGlobals;
+
+class CollatorTest extends AbstractCollatorTest
+{
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException
+     */
+    public function testConstructorWithUnsupportedLocale()
+    {
+        new Collator('pt_BR');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testCompare()
+    {
+        $collator = $this->getCollator('en');
+        $collator->compare('a', 'b');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testGetAttribute()
+    {
+        $collator = $this->getCollator('en');
+        $collator->getAttribute(Collator::NUMERIC_COLLATION);
+    }
+
+    public function testGetErrorCode()
+    {
+        $collator = $this->getCollator('en');
+        $this->assertEquals(IntlGlobals::U_ZERO_ERROR, $collator->getErrorCode());
+    }
+
+    public function testGetErrorMessage()
+    {
+        $collator = $this->getCollator('en');
+        $this->assertEquals('U_ZERO_ERROR', $collator->getErrorMessage());
+    }
+
+    public function testGetLocale()
+    {
+        $collator = $this->getCollator('en');
+        $this->assertEquals('en', $collator->getLocale());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testGetSortKey()
+    {
+        $collator = $this->getCollator('en');
+        $collator->getSortKey('Hello');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testGetStrength()
+    {
+        $collator = $this->getCollator('en');
+        $collator->getStrength();
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testSetAttribute()
+    {
+        $collator = $this->getCollator('en');
+        $collator->setAttribute(Collator::NUMERIC_COLLATION, Collator::ON);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testSetStrength()
+    {
+        $collator = $this->getCollator('en');
+        $collator->setStrength(Collator::PRIMARY);
+    }
+
+    public function testStaticCreate()
+    {
+        $collator = Collator::create('en');
+        $this->assertInstanceOf('\Symfony\Component\Intl\Collator\Collator', $collator);
+    }
+
+    protected function getCollator($locale)
+    {
+        return new Collator($locale);
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/Collator/Verification/CollatorTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/Collator/Verification/CollatorTest.php
new file mode 100644 (file)
index 0000000..c8dbc13
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\Collator\Verification;
+
+use Symfony\Component\Intl\Locale;
+use Symfony\Component\Intl\Tests\Collator\AbstractCollatorTest;
+use Symfony\Component\Intl\Util\IntlTestHelper;
+
+/**
+ * Verifies that {@link AbstractCollatorTest} matches the behavior of the
+ * {@link \Collator} class in a specific version of ICU.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class CollatorTest extends AbstractCollatorTest
+{
+    protected function setUp()
+    {
+        IntlTestHelper::requireFullIntl($this);
+
+        parent::setUp();
+    }
+
+    protected function getCollator($locale)
+    {
+        return new \Collator($locale);
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/DateFormatter/AbstractIntlDateFormatterTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/DateFormatter/AbstractIntlDateFormatterTest.php
new file mode 100644 (file)
index 0000000..3ffb490
--- /dev/null
@@ -0,0 +1,932 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\DateFormatter;
+
+use Symfony\Component\Intl\DateFormatter\IntlDateFormatter;
+use Symfony\Component\Intl\Globals\IntlGlobals;
+use Symfony\Component\Intl\Intl;
+
+/**
+ * Test case for IntlDateFormatter implementations.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+abstract class AbstractIntlDateFormatterTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * When a time zone is not specified, it uses the system default however it returns null in the getter method
+     * @covers Symfony\Component\Intl\DateFormatter\IntlDateFormatter::getTimeZoneId
+     * @see StubIntlDateFormatterTest::testDefaultTimeZoneIntl()
+     */
+    public function testConstructorDefaultTimeZone()
+    {
+        $formatter = $this->getDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT);
+
+        // In PHP 5.5 default timezone depends on `date_default_timezone_get()` method
+        if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
+            $this->assertEquals(date_default_timezone_get(), $formatter->getTimeZoneId());
+        } else {
+            $this->assertNull($formatter->getTimeZoneId());
+        }
+    }
+
+    /**
+     * @dataProvider formatProvider
+     */
+    public function testFormat($pattern, $timestamp, $expected)
+    {
+        $errorCode = IntlGlobals::U_ZERO_ERROR;
+        $errorMessage = 'U_ZERO_ERROR';
+
+        $formatter = $this->getDefaultDateFormatter($pattern);
+        $this->assertSame($expected, $formatter->format($timestamp));
+        $this->assertIsIntlSuccess($formatter, $errorMessage, $errorCode);
+    }
+
+    public function formatProvider()
+    {
+        $formatData = array(
+            /* general */
+            array('y-M-d', 0, '1970-1-1'),
+            array("EEE, MMM d, ''yy", 0, "Thu, Jan 1, '70"),
+            array('h:mm a', 0, '12:00 AM'),
+            array('yyyyy.MMMM.dd hh:mm aaa', 0, '01970.January.01 12:00 AM'),
+
+            /* escaping */
+            array("'M'", 0, 'M'),
+            array("'yy'", 0, 'yy'),
+            array("'''yy'", 0, "'yy"),
+            array("''y", 0, "'1970"),
+            array("''yy", 0, "'70"),
+            array("H 'o'' clock'", 0, "0 o' clock"),
+
+            /* month */
+            array('M', 0, '1'),
+            array('MM', 0, '01'),
+            array('MMM', 0, 'Jan'),
+            array('MMMM', 0, 'January'),
+            array('MMMMM', 0, 'J'),
+            array('MMMMMM', 0, '000001'),
+
+            array('L', 0, '1'),
+            array('LL', 0, '01'),
+            array('LLL', 0, 'Jan'),
+            array('LLLL', 0, 'January'),
+            array('LLLLL', 0, 'J'),
+            array('LLLLLL', 0, '000001'),
+
+            /* year */
+            array('y', 0, '1970'),
+            array('yy', 0, '70'),
+            array('yyy', 0, '1970'),
+            array('yyyy', 0, '1970'),
+            array('yyyyy', 0, '01970'),
+            array('yyyyyy', 0, '001970'),
+
+            /* day */
+            array('d', 0, '1'),
+            array('dd', 0, '01'),
+            array('ddd', 0, '001'),
+
+            /* quarter */
+            array('Q', 0, '1'),
+            array('QQ', 0, '01'),
+            array('QQQ', 0, 'Q1'),
+            array('QQQQ', 0, '1st quarter'),
+            array('QQQQQ', 0, '1st quarter'),
+
+            array('q', 0, '1'),
+            array('qq', 0, '01'),
+            array('qqq', 0, 'Q1'),
+            array('qqqq', 0, '1st quarter'),
+            array('qqqqq', 0, '1st quarter'),
+
+            // 4 months
+            array('Q', 7776000, '2'),
+            array('QQ', 7776000, '02'),
+            array('QQQ', 7776000, 'Q2'),
+            array('QQQQ', 7776000, '2nd quarter'),
+
+            // 7 months
+            array('QQQQ', 15638400, '3rd quarter'),
+
+            // 10 months
+            array('QQQQ', 23587200, '4th quarter'),
+
+            /* 12-hour (1-12) */
+            array('h', 0, '12'),
+            array('hh', 0, '12'),
+            array('hhh', 0, '012'),
+
+            array('h', 1, '12'),
+            array('h', 3600, '1'),
+            array('h', 43200, '12'), // 12 hours
+
+            /* day of year */
+            array('D', 0, '1'),
+            array('D', 86400, '2'), // 1 day
+            array('D', 31536000, '1'), // 1 year
+            array('D', 31622400, '2'), // 1 year + 1 day
+
+            /* day of week */
+            array('E', 0, 'Thu'),
+            array('EE', 0, 'Thu'),
+            array('EEE', 0, 'Thu'),
+            array('EEEE', 0, 'Thursday'),
+            array('EEEEE', 0, 'T'),
+            array('EEEEEE', 0, 'Thu'),
+
+            array('E', 1296540000, 'Tue'), // 2011-02-01
+            array('E', 1296950400, 'Sun'), // 2011-02-06
+
+            /* am/pm marker */
+            array('a', 0, 'AM'),
+            array('aa', 0, 'AM'),
+            array('aaa', 0, 'AM'),
+            array('aaaa', 0, 'AM'),
+
+            // 12 hours
+            array('a', 43200, 'PM'),
+            array('aa', 43200, 'PM'),
+            array('aaa', 43200, 'PM'),
+            array('aaaa', 43200, 'PM'),
+
+            /* 24-hour (0-23) */
+            array('H', 0, '0'),
+            array('HH', 0, '00'),
+            array('HHH', 0, '000'),
+
+            array('H', 1, '0'),
+            array('H', 3600, '1'),
+            array('H', 43200, '12'),
+            array('H', 46800, '13'),
+
+            /* 24-hour (1-24) */
+            array('k', 0, '24'),
+            array('kk', 0, '24'),
+            array('kkk', 0, '024'),
+
+            array('k', 1, '24'),
+            array('k', 3600, '1'),
+            array('k', 43200, '12'),
+            array('k', 46800, '13'),
+
+            /* 12-hour (0-11) */
+            array('K', 0, '0'),
+            array('KK', 0, '00'),
+            array('KKK', 0, '000'),
+
+            array('K', 1, '0'),
+            array('K', 3600, '1'),
+            array('K', 43200, '0'), // 12 hours
+
+            /* minute */
+            array('m', 0, '0'),
+            array('mm', 0, '00'),
+            array('mmm', 0, '000'),
+
+            array('m', 1, '0'),
+            array('m', 60, '1'),
+            array('m', 120, '2'),
+            array('m', 180, '3'),
+            array('m', 3600, '0'),
+            array('m', 3660, '1'),
+            array('m', 43200, '0'), // 12 hours
+
+            /* second */
+            array('s', 0, '0'),
+            array('ss', 0, '00'),
+            array('sss', 0, '000'),
+
+            array('s', 1, '1'),
+            array('s', 2, '2'),
+            array('s', 5, '5'),
+            array('s', 30, '30'),
+            array('s', 59, '59'),
+            array('s', 60, '0'),
+            array('s', 120, '0'),
+            array('s', 180, '0'),
+            array('s', 3600, '0'),
+            array('s', 3601, '1'),
+            array('s', 3630, '30'),
+            array('s', 43200, '0'), // 12 hours
+
+            // general
+            array("yyyy.MM.dd 'at' HH:mm:ss zzz", 0, '1970.01.01 at 00:00:00 GMT'),
+            array('K:mm a, z', 0, '0:00 AM, GMT'),
+
+            // timezone
+            array('z', 0, 'GMT'),
+            array('zz', 0, 'GMT'),
+            array('zzz', 0, 'GMT'),
+            array('zzzz', 0, 'GMT'),
+            array('zzzzz', 0, 'GMT'),
+        );
+
+        // As of PHP 5.3.4, IntlDateFormatter::format() accepts DateTime instances
+        if (version_compare(PHP_VERSION, '5.3.4', '>=')) {
+            $dateTime = new \DateTime('@0');
+
+            /* general, DateTime */
+            $formatData[] = array('y-M-d', $dateTime, '1970-1-1');
+            $formatData[] = array("EEE, MMM d, ''yy", $dateTime, "Thu, Jan 1, '70");
+            $formatData[] = array('h:mm a', $dateTime, '12:00 AM');
+            $formatData[] = array('yyyyy.MMMM.dd hh:mm aaa', $dateTime, '01970.January.01 12:00 AM');
+
+            $formatData[] = array("yyyy.MM.dd 'at' HH:mm:ss zzz", $dateTime, '1970.01.01 at 00:00:00 GMT');
+            $formatData[] = array('K:mm a, z', $dateTime, '0:00 AM, GMT');
+        }
+
+        return $formatData;
+    }
+
+    /**
+     * @dataProvider formatErrorProvider
+     */
+    public function testFormatIllegalArgumentError($pattern, $timestamp, $errorMessage)
+    {
+        $errorCode = IntlGlobals::U_ILLEGAL_ARGUMENT_ERROR;
+
+        $formatter = $this->getDefaultDateFormatter($pattern);
+        $this->assertFalse($formatter->format($timestamp));
+        $this->assertIsIntlFailure($formatter, $errorMessage, $errorCode);
+    }
+
+    public function formatErrorProvider()
+    {
+        // With PHP 5.5 IntlDateFormatter accepts empty values ('0')
+        if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
+            return array(
+                array('y-M-d', 'foobar', 'datefmt_format: string \'foobar\' is not numeric, which would be required for it to be a valid date: U_ILLEGAL_ARGUMENT_ERROR')
+            );
+        }
+
+        $message = 'datefmt_format: takes either an array  or an integer timestamp value : U_ILLEGAL_ARGUMENT_ERROR';
+
+        if (version_compare(PHP_VERSION, '5.3.4', '>=')) {
+            $message = 'datefmt_format: takes either an array or an integer timestamp value or a DateTime object: U_ILLEGAL_ARGUMENT_ERROR';
+        }
+
+        return array(
+            array('y-M-d', '0', $message),
+            array('y-M-d', 'foobar', $message),
+        );
+    }
+
+    /**
+     * @dataProvider formatWithTimezoneProvider
+     */
+    public function testFormatWithTimezone($timestamp, $timezone, $expected)
+    {
+        $pattern = 'yyyy-MM-dd HH:mm:ss';
+        $formatter = $this->getDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT, $timezone, IntlDateFormatter::GREGORIAN, $pattern);
+        $this->assertSame($expected, $formatter->format($timestamp));
+    }
+
+    public function formatWithTimezoneProvider()
+    {
+        $data = array(
+            array(0, 'UTC', '1970-01-01 00:00:00'),
+            array(0, 'GMT', '1970-01-01 00:00:00'),
+            array(0, 'GMT-03:00', '1969-12-31 21:00:00'),
+            array(0, 'GMT+03:00', '1970-01-01 03:00:00'),
+            array(0, 'Europe/Zurich', '1970-01-01 01:00:00'),
+            array(0, 'Europe/Paris', '1970-01-01 01:00:00'),
+            array(0, 'Africa/Cairo', '1970-01-01 02:00:00'),
+            array(0, 'Africa/Casablanca', '1970-01-01 00:00:00'),
+            array(0, 'Africa/Djibouti', '1970-01-01 03:00:00'),
+            array(0, 'Africa/Johannesburg', '1970-01-01 02:00:00'),
+            array(0, 'America/Antigua', '1969-12-31 20:00:00'),
+            array(0, 'America/Toronto', '1969-12-31 19:00:00'),
+            array(0, 'America/Vancouver', '1969-12-31 16:00:00'),
+            array(0, 'Asia/Aqtau', '1970-01-01 05:00:00'),
+            array(0, 'Asia/Bangkok', '1970-01-01 07:00:00'),
+            array(0, 'Asia/Dubai', '1970-01-01 04:00:00'),
+            array(0, 'Australia/Brisbane', '1970-01-01 10:00:00'),
+            array(0, 'Australia/Eucla', '1970-01-01 08:45:00'),
+            array(0, 'Australia/Melbourne', '1970-01-01 10:00:00'),
+            array(0, 'Europe/Berlin', '1970-01-01 01:00:00'),
+            array(0, 'Europe/Dublin', '1970-01-01 01:00:00'),
+            array(0, 'Europe/Warsaw', '1970-01-01 01:00:00'),
+            array(0, 'Pacific/Fiji', '1970-01-01 12:00:00'),
+        );
+
+        // As of PHP 5.5, intl ext no longer fallbacks invalid time zones to UTC
+        if (!version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
+            // When time zone not exists, uses UTC by default
+            $data[] = array(0, 'Foo/Bar', '1970-01-01 00:00:00');
+            $data[] = array(0, 'UTC+04:30', '1970-01-01 00:00:00');
+            $data[] = array(0, 'UTC+04:AA', '1970-01-01 00:00:00');
+        }
+
+        return $data;
+    }
+
+    public function testFormatWithGmtTimezone()
+    {
+        $formatter = $this->getDefaultDateFormatter('zzzz');
+
+        if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
+            $formatter->setTimeZone('GMT+03:00');
+        } else {
+            $formatter->setTimeZoneId('GMT+03:00');
+        }
+
+        $this->assertEquals('GMT+03:00', $formatter->format(0));
+    }
+
+    public function testFormatWithGmtTimeZoneAndMinutesOffset()
+    {
+        $formatter = $this->getDefaultDateFormatter('zzzz');
+
+        if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
+            $formatter->setTimeZone('GMT+00:30');
+        } else {
+            $formatter->setTimeZoneId('GMT+00:30');
+        }
+
+        $this->assertEquals('GMT+00:30', $formatter->format(0));
+    }
+
+    public function testFormatWithNonStandardTimezone()
+    {
+        $formatter = $this->getDefaultDateFormatter('zzzz');
+
+        if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
+            $formatter->setTimeZone('Pacific/Fiji');
+        } else {
+            $formatter->setTimeZoneId('Pacific/Fiji');
+        }
+
+        $this->assertEquals('Fiji Standard Time', $formatter->format(0));
+    }
+
+    public function testFormatWithConstructorTimezone()
+    {
+        $formatter = $this->getDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT, 'UTC');
+        $formatter->setPattern('yyyy-MM-dd HH:mm:ss');
+
+        $this->assertEquals(
+            $this->getDateTime(0)->format('Y-m-d H:i:s'),
+            $formatter->format(0)
+        );
+    }
+
+    public function testFormatWithTimezoneFromEnvironmentVariable()
+    {
+        if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
+            $this->markTestSkipped('IntlDateFormatter in PHP 5.5 no longer depends on TZ environment.');
+        }
+
+        $tz = getenv('TZ');
+        putenv('TZ=Europe/London');
+
+        $formatter = $this->getDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT);
+        $formatter->setPattern('yyyy-MM-dd HH:mm:ss');
+
+        $this->assertEquals(
+            $this->getDateTime(0)->format('Y-m-d H:i:s'),
+            $formatter->format(0)
+        );
+
+        $this->assertEquals('Europe/London', getenv('TZ'));
+
+        // Restores TZ.
+        putenv('TZ='.$tz);
+    }
+
+    public function testFormatWithTimezoneFromPhp()
+    {
+        if (!version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
+            $this->markTestSkipped('Only in PHP 5.5 IntlDateFormatter depends on default timezone (`date_default_timezone_get()`).');
+        }
+
+        $tz = date_default_timezone_get();
+        date_default_timezone_set('Europe/London');
+
+        $formatter = $this->getDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT);
+        $formatter->setPattern('yyyy-MM-dd HH:mm:ss');
+
+        $this->assertEquals(
+            $this->getDateTime(0)->format('Y-m-d H:i:s'),
+            $formatter->format(0)
+        );
+
+        $this->assertEquals('Europe/London', date_default_timezone_get());
+
+        // Restores TZ.
+        date_default_timezone_set($tz);
+    }
+
+    /**
+     * @dataProvider dateAndTimeTypeProvider
+     */
+    public function testDateAndTimeType($timestamp, $datetype, $timetype, $expected)
+    {
+        $formatter = $this->getDateFormatter('en', $datetype, $timetype, 'UTC');
+        $this->assertSame($expected, $formatter->format($timestamp));
+    }
+
+    public function dateAndTimeTypeProvider()
+    {
+        return array(
+            array(0, IntlDateFormatter::FULL, IntlDateFormatter::NONE, 'Thursday, January 1, 1970'),
+            array(0, IntlDateFormatter::LONG, IntlDateFormatter::NONE, 'January 1, 1970'),
+            array(0, IntlDateFormatter::MEDIUM, IntlDateFormatter::NONE, 'Jan 1, 1970'),
+            array(0, IntlDateFormatter::SHORT, IntlDateFormatter::NONE, '1/1/70'),
+            array(0, IntlDateFormatter::NONE, IntlDateFormatter::FULL, '12:00:00 AM GMT'),
+            array(0, IntlDateFormatter::NONE, IntlDateFormatter::LONG, '12:00:00 AM GMT'),
+            array(0, IntlDateFormatter::NONE, IntlDateFormatter::MEDIUM, '12:00:00 AM'),
+            array(0, IntlDateFormatter::NONE, IntlDateFormatter::SHORT, '12:00 AM'),
+        );
+    }
+
+    public function testGetCalendar()
+    {
+        $formatter = $this->getDefaultDateFormatter();
+        $this->assertEquals(IntlDateFormatter::GREGORIAN, $formatter->getCalendar());
+    }
+
+    public function testGetDateType()
+    {
+        $formatter = $this->getDateFormatter('en', IntlDateFormatter::FULL, IntlDateFormatter::NONE);
+        $this->assertEquals(IntlDateFormatter::FULL, $formatter->getDateType());
+    }
+
+    public function testGetLocale()
+    {
+        $formatter = $this->getDefaultDateFormatter();
+        $this->assertEquals('en', $formatter->getLocale());
+    }
+
+    public function testGetPattern()
+    {
+        $formatter = $this->getDateFormatter('en', IntlDateFormatter::FULL, IntlDateFormatter::NONE, 'UTC', IntlDateFormatter::GREGORIAN, 'yyyy-MM-dd');
+        $this->assertEquals('yyyy-MM-dd', $formatter->getPattern());
+    }
+
+    public function testGetTimeType()
+    {
+        $formatter = $this->getDateFormatter('en', IntlDateFormatter::NONE, IntlDateFormatter::FULL);
+        $this->assertEquals(IntlDateFormatter::FULL, $formatter->getTimeType());
+    }
+
+    /**
+     * @dataProvider parseProvider
+     */
+    public function testParse($pattern, $value, $expected)
+    {
+        $errorCode = IntlGlobals::U_ZERO_ERROR;
+        $errorMessage = 'U_ZERO_ERROR';
+
+        $formatter = $this->getDefaultDateFormatter($pattern);
+        $this->assertSame($expected, $formatter->parse($value));
+        $this->assertIsIntlSuccess($formatter, $errorMessage, $errorCode);
+    }
+
+    public function parseProvider()
+    {
+        return array_merge(
+            $this->parseYearProvider(),
+            $this->parseQuarterProvider(),
+            $this->parseMonthProvider(),
+            $this->parseStandaloneMonthProvider(),
+            $this->parseDayProvider(),
+            $this->parseDayOfWeekProvider(),
+            $this->parseDayOfYearProvider(),
+            $this->parseHour12ClockOneBasedProvider(),
+            $this->parseHour12ClockZeroBasedProvider(),
+            $this->parseHour24ClockOneBasedProvider(),
+            $this->parseHour24ClockZeroBasedProvider(),
+            $this->parseMinuteProvider(),
+            $this->parseSecondProvider(),
+            $this->parseTimezoneProvider(),
+            $this->parseAmPmProvider(),
+            $this->parseStandaloneAmPmProvider(),
+            $this->parseRegexMetaCharsProvider(),
+            $this->parseQuoteCharsProvider(),
+            $this->parseDashSlashProvider()
+        );
+    }
+
+    public function parseYearProvider()
+    {
+        return array(
+            array('y-M-d', '1970-1-1', 0),
+            array('yy-M-d', '70-1-1', 0),
+        );
+    }
+
+    public function parseQuarterProvider()
+    {
+        return array(
+            array('Q', '1', 0),
+            array('QQ', '01', 0),
+            array('QQQ', 'Q1', 0),
+            array('QQQQ', '1st quarter', 0),
+            array('QQQQQ', '1st quarter', 0),
+
+            array('Q', '2', 7776000),
+            array('QQ', '02', 7776000),
+            array('QQQ', 'Q2', 7776000),
+            array('QQQQ', '2nd quarter', 7776000),
+            array('QQQQQ', '2nd quarter', 7776000),
+
+            array('q', '1', 0),
+            array('qq', '01', 0),
+            array('qqq', 'Q1', 0),
+            array('qqqq', '1st quarter', 0),
+            array('qqqqq', '1st quarter', 0),
+        );
+    }
+
+    public function parseMonthProvider()
+    {
+        return array(
+            array('y-M-d', '1970-1-1', 0),
+            array('y-MMM-d', '1970-Jan-1', 0),
+            array('y-MMMM-d', '1970-January-1', 0),
+        );
+    }
+
+    public function parseStandaloneMonthProvider()
+    {
+        return array(
+            array('y-L-d', '1970-1-1', 0),
+            array('y-LLL-d', '1970-Jan-1', 0),
+            array('y-LLLL-d', '1970-January-1', 0),
+        );
+    }
+
+    public function parseDayProvider()
+    {
+        return array(
+            array('y-M-d', '1970-1-1', 0),
+            array('y-M-dd', '1970-1-01', 0),
+            array('y-M-ddd', '1970-1-001', 0),
+        );
+    }
+
+    public function parseDayOfWeekProvider()
+    {
+        return array(
+            array('E', 'Thu', 0),
+            array('EE', 'Thu', 0),
+            array('EEE', 'Thu', 0),
+            array('EEEE', 'Thursday', 0),
+            array('EEEEE', 'T', 432000),
+            array('EEEEEE', 'Thu', 0),
+        );
+    }
+
+    public function parseDayOfYearProvider()
+    {
+        return array(
+            array('D', '1', 0),
+            array('D', '2', 86400),
+        );
+    }
+
+    public function parseHour12ClockOneBasedProvider()
+    {
+        return array(
+            // 12 hours (1-12)
+            array('y-M-d h', '1970-1-1 1', 3600),
+            array('y-M-d h', '1970-1-1 10', 36000),
+            array('y-M-d hh', '1970-1-1 11', 39600),
+            array('y-M-d hh', '1970-1-1 12', 0),
+            array('y-M-d hh a', '1970-1-1 0 AM', 0),
+            array('y-M-d hh a', '1970-1-1 1 AM', 3600),
+            array('y-M-d hh a', '1970-1-1 10 AM', 36000),
+            array('y-M-d hh a', '1970-1-1 11 AM', 39600),
+            array('y-M-d hh a', '1970-1-1 12 AM', 0),
+            array('y-M-d hh a', '1970-1-1 23 AM', 82800),
+            array('y-M-d hh a', '1970-1-1 24 AM', 86400),
+            array('y-M-d hh a', '1970-1-1 0 PM', 43200),
+            array('y-M-d hh a', '1970-1-1 1 PM', 46800),
+            array('y-M-d hh a', '1970-1-1 10 PM', 79200),
+            array('y-M-d hh a', '1970-1-1 11 PM', 82800),
+            array('y-M-d hh a', '1970-1-1 12 PM', 43200),
+            array('y-M-d hh a', '1970-1-1 23 PM', 126000),
+            array('y-M-d hh a', '1970-1-1 24 PM', 129600),
+        );
+    }
+
+    public function parseHour12ClockZeroBasedProvider()
+    {
+        return array(
+            // 12 hours (0-11)
+            array('y-M-d K', '1970-1-1 1', 3600),
+            array('y-M-d K', '1970-1-1 10', 36000),
+            array('y-M-d KK', '1970-1-1 11', 39600),
+            array('y-M-d KK', '1970-1-1 12', 43200),
+            array('y-M-d KK a', '1970-1-1 0 AM', 0),
+            array('y-M-d KK a', '1970-1-1 1 AM', 3600),
+            array('y-M-d KK a', '1970-1-1 10 AM', 36000),
+            array('y-M-d KK a', '1970-1-1 11 AM', 39600),
+            array('y-M-d KK a', '1970-1-1 12 AM', 43200),
+            array('y-M-d KK a', '1970-1-1 23 AM', 82800),
+            array('y-M-d KK a', '1970-1-1 24 AM', 86400),
+            array('y-M-d KK a', '1970-1-1 0 PM', 43200),
+            array('y-M-d KK a', '1970-1-1 1 PM', 46800),
+            array('y-M-d KK a', '1970-1-1 10 PM', 79200),
+            array('y-M-d KK a', '1970-1-1 11 PM', 82800),
+            array('y-M-d KK a', '1970-1-1 12 PM', 86400),
+            array('y-M-d KK a', '1970-1-1 23 PM', 126000),
+            array('y-M-d KK a', '1970-1-1 24 PM', 129600),
+        );
+    }
+
+    public function parseHour24ClockOneBasedProvider()
+    {
+        return array(
+            // 24 hours (1-24)
+            array('y-M-d k', '1970-1-1 1', 3600),
+            array('y-M-d k', '1970-1-1 10', 36000),
+            array('y-M-d kk', '1970-1-1 11', 39600),
+            array('y-M-d kk', '1970-1-1 12', 43200),
+            array('y-M-d kk', '1970-1-1 23', 82800),
+            array('y-M-d kk', '1970-1-1 24', 0),
+            array('y-M-d kk a', '1970-1-1 0 AM', 0),
+            array('y-M-d kk a', '1970-1-1 1 AM', 0),
+            array('y-M-d kk a', '1970-1-1 10 AM', 0),
+            array('y-M-d kk a', '1970-1-1 11 AM', 0),
+            array('y-M-d kk a', '1970-1-1 12 AM', 0),
+            array('y-M-d kk a', '1970-1-1 23 AM', 0),
+            array('y-M-d kk a', '1970-1-1 24 AM', 0),
+            array('y-M-d kk a', '1970-1-1 0 PM', 43200),
+            array('y-M-d kk a', '1970-1-1 1 PM', 43200),
+            array('y-M-d kk a', '1970-1-1 10 PM', 43200),
+            array('y-M-d kk a', '1970-1-1 11 PM', 43200),
+            array('y-M-d kk a', '1970-1-1 12 PM', 43200),
+            array('y-M-d kk a', '1970-1-1 23 PM', 43200),
+            array('y-M-d kk a', '1970-1-1 24 PM', 43200),
+        );
+    }
+
+    public function parseHour24ClockZeroBasedProvider()
+    {
+        return array(
+            // 24 hours (0-23)
+            array('y-M-d H', '1970-1-1 0', 0),
+            array('y-M-d H', '1970-1-1 1', 3600),
+            array('y-M-d H', '1970-1-1 10', 36000),
+            array('y-M-d HH', '1970-1-1 11', 39600),
+            array('y-M-d HH', '1970-1-1 12', 43200),
+            array('y-M-d HH', '1970-1-1 23', 82800),
+            array('y-M-d HH a', '1970-1-1 0 AM', 0),
+            array('y-M-d HH a', '1970-1-1 1 AM', 0),
+            array('y-M-d HH a', '1970-1-1 10 AM', 0),
+            array('y-M-d HH a', '1970-1-1 11 AM', 0),
+            array('y-M-d HH a', '1970-1-1 12 AM', 0),
+            array('y-M-d HH a', '1970-1-1 23 AM', 0),
+            array('y-M-d HH a', '1970-1-1 24 AM', 0),
+            array('y-M-d HH a', '1970-1-1 0 PM', 43200),
+            array('y-M-d HH a', '1970-1-1 1 PM', 43200),
+            array('y-M-d HH a', '1970-1-1 10 PM', 43200),
+            array('y-M-d HH a', '1970-1-1 11 PM', 43200),
+            array('y-M-d HH a', '1970-1-1 12 PM', 43200),
+            array('y-M-d HH a', '1970-1-1 23 PM', 43200),
+            array('y-M-d HH a', '1970-1-1 24 PM', 43200),
+        );
+    }
+
+    public function parseMinuteProvider()
+    {
+        return array(
+            array('y-M-d HH:m', '1970-1-1 0:1', 60),
+            array('y-M-d HH:mm', '1970-1-1 0:10', 600),
+        );
+    }
+
+    public function parseSecondProvider()
+    {
+        return array(
+            array('y-M-d HH:mm:s', '1970-1-1 00:01:1', 61),
+            array('y-M-d HH:mm:ss', '1970-1-1 00:01:10', 70),
+        );
+    }
+
+    public function parseTimezoneProvider()
+    {
+        return array(
+            array('y-M-d HH:mm:ss zzzz', '1970-1-1 00:00:00 GMT-03:00', 10800),
+            array('y-M-d HH:mm:ss zzzz', '1970-1-1 00:00:00 GMT-04:00', 14400),
+            array('y-M-d HH:mm:ss zzzz', '1970-1-1 00:00:00 GMT-00:00', 0),
+            array('y-M-d HH:mm:ss zzzz', '1970-1-1 00:00:00 GMT+03:00', -10800),
+            array('y-M-d HH:mm:ss zzzz', '1970-1-1 00:00:00 GMT+04:00', -14400),
+            array('y-M-d HH:mm:ss zzzz', '1970-1-1 00:00:00 GMT-0300', 10800),
+            array('y-M-d HH:mm:ss zzzz', '1970-1-1 00:00:00 GMT+0300', -10800),
+
+            // a previous timezone parsing should not change the timezone for the next parsing
+            array('y-M-d HH:mm:ss', '1970-1-1 00:00:00', 0),
+        );
+    }
+
+    public function parseAmPmProvider()
+    {
+        return array(
+            // AM/PM (already covered by hours tests)
+            array('y-M-d HH:mm:ss a', '1970-1-1 00:00:00 AM', 0),
+            array('y-M-d HH:mm:ss a', '1970-1-1 00:00:00 PM', 43200),
+        );
+    }
+
+    public function parseStandaloneAmPmProvider()
+    {
+        return array(
+            array('a', 'AM', 0),
+            array('a', 'PM', 43200),
+        );
+    }
+
+    public function parseRegexMetaCharsProvider()
+    {
+        return array(
+            // regexp meta chars in the pattern string
+            array('y[M-d', '1970[1-1', 0),
+            array('y[M/d', '1970[1/1', 0),
+        );
+    }
+
+    public function parseQuoteCharsProvider()
+    {
+        return array(
+            array("'M'", 'M', 0),
+            array("'yy'", 'yy', 0),
+            array("'''yy'", "'yy", 0),
+            array("''y", "'1970", 0),
+            array("H 'o'' clock'", "0 o' clock", 0),
+        );
+    }
+
+    public function parseDashSlashProvider()
+    {
+        return array(
+            array('y-M-d', '1970/1/1', 0),
+            array('yy-M-d', '70/1/1', 0),
+            array('y/M/d', '1970-1-1', 0),
+            array('yy/M/d', '70-1-1', 0),
+        );
+    }
+
+    /**
+     * @dataProvider parseErrorProvider
+     */
+    public function testParseError($pattern, $value)
+    {
+        $errorCode = IntlGlobals::U_PARSE_ERROR;
+        $errorMessage = 'Date parsing failed: U_PARSE_ERROR';
+
+        $formatter = $this->getDefaultDateFormatter($pattern);
+        $this->assertFalse($formatter->parse($value));
+        $this->assertIsIntlFailure($formatter, $errorMessage, $errorCode);
+    }
+
+    public function parseErrorProvider()
+    {
+        return array(
+            // 1 char month
+            array('y-MMMMM-d', '1970-J-1'),
+            array('y-MMMMM-d', '1970-S-1'),
+
+            // standalone 1 char month
+            array('y-LLLLL-d', '1970-J-1'),
+            array('y-LLLLL-d', '1970-S-1'),
+        );
+    }
+
+    /*
+     * https://github.com/symfony/symfony/issues/4242
+     */
+    public function testParseAfterError()
+    {
+        $this->testParseError('y-MMMMM-d', '1970-J-1');
+        $this->testParse('y-M-d', '1970-1-1', 0);
+    }
+
+    public function testParseWithNullPositionValue()
+    {
+        $position = null;
+        $formatter = $this->getDefaultDateFormatter('y');
+        $this->assertSame(0, $formatter->parse('1970', $position));
+        $this->assertNull($position);
+    }
+
+    public function testSetPattern()
+    {
+        $formatter = $this->getDefaultDateFormatter();
+        $formatter->setPattern('yyyy-MM-dd');
+        $this->assertEquals('yyyy-MM-dd', $formatter->getPattern());
+    }
+
+    /**
+     * @covers Symfony\Component\Intl\DateFormatter\IntlDateFormatter::getTimeZoneId
+     * @dataProvider setTimeZoneIdProvider
+     */
+    public function testSetTimeZoneId($timeZoneId, $expectedTimeZoneId)
+    {
+        $formatter = $this->getDefaultDateFormatter();
+
+        if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
+            $formatter->setTimeZone($timeZoneId);
+        } else {
+            $formatter->setTimeZoneId($timeZoneId);
+        }
+
+        $this->assertEquals($expectedTimeZoneId, $formatter->getTimeZoneId());
+    }
+
+    public function setTimeZoneIdProvider()
+    {
+        return array(
+            array('UTC', 'UTC'),
+            array('GMT', 'GMT'),
+            array('GMT-03:00', 'GMT-03:00'),
+            array('Europe/Zurich', 'Europe/Zurich'),
+            array('GMT-0300', 'GMT-0300'),
+            array('Foo/Bar', 'Foo/Bar'),
+            array('GMT+00:AA', 'GMT+00:AA'),
+            array('GMT+00AA', 'GMT+00AA'),
+        );
+    }
+
+    protected function getDefaultDateFormatter($pattern = null)
+    {
+        return $this->getDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT, 'UTC', IntlDateFormatter::GREGORIAN, $pattern);
+    }
+
+    protected function getDateTime($timestamp = null)
+    {
+        if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
+            $timeZone = date_default_timezone_get();
+        } else {
+            $timeZone = getenv('TZ') ?: 'UTC';
+        }
+
+        $dateTime = new \DateTime();
+        $dateTime->setTimestamp(null === $timestamp ? time() : $timestamp);
+        $dateTime->setTimeZone(new \DateTimeZone($timeZone));
+
+        return $dateTime;
+    }
+
+    protected function assertIsIntlFailure($formatter, $errorMessage, $errorCode)
+    {
+        $this->assertSame($errorMessage, $this->getIntlErrorMessage());
+        $this->assertSame($errorCode, $this->getIntlErrorCode());
+        $this->assertTrue($this->isIntlFailure($this->getIntlErrorCode()));
+        $this->assertSame($errorMessage, $formatter->getErrorMessage());
+        $this->assertSame($errorCode, $formatter->getErrorCode());
+        $this->assertTrue($this->isIntlFailure($formatter->getErrorCode()));
+    }
+
+    protected function assertIsIntlSuccess($formatter, $errorMessage, $errorCode)
+    {
+        /* @var IntlDateFormatter $formatter */
+        $this->assertSame($errorMessage, $this->getIntlErrorMessage());
+        $this->assertSame($errorCode, $this->getIntlErrorCode());
+        $this->assertFalse($this->isIntlFailure($this->getIntlErrorCode()));
+        $this->assertSame($errorMessage, $formatter->getErrorMessage());
+        $this->assertSame($errorCode, $formatter->getErrorCode());
+        $this->assertFalse($this->isIntlFailure($formatter->getErrorCode()));
+    }
+
+    /**
+     * @param $locale
+     * @param $datetype
+     * @param $timetype
+     * @param null $timezone
+     * @param int $calendar
+     * @param null $pattern
+     *
+     * @return mixed
+     */
+    abstract protected function getDateFormatter($locale, $datetype, $timetype, $timezone = null, $calendar = IntlDateFormatter::GREGORIAN, $pattern = null);
+
+    /**
+     * @return string
+     */
+    abstract protected function getIntlErrorMessage();
+
+    /**
+     * @return integer
+     */
+    abstract protected function getIntlErrorCode();
+
+    /**
+     * @param integer $errorCode
+     *
+     * @return Boolean
+     */
+    abstract protected function isIntlFailure($errorCode);
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/DateFormatter/IntlDateFormatterTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/DateFormatter/IntlDateFormatterTest.php
new file mode 100644 (file)
index 0000000..d7227ae
--- /dev/null
@@ -0,0 +1,220 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\DateFormatter;
+
+use Symfony\Component\Intl\DateFormatter\IntlDateFormatter;
+use Symfony\Component\Intl\Globals\IntlGlobals;
+
+class IntlDateFormatterTest extends AbstractIntlDateFormatterTest
+{
+    public function testConstructor()
+    {
+        $formatter = new IntlDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT, 'UTC', IntlDateFormatter::GREGORIAN, 'y-M-d');
+        $this->assertEquals('y-M-d', $formatter->getPattern());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException
+     */
+    public function testConstructorWithUnsupportedLocale()
+    {
+        new IntlDateFormatter('pt_BR', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT);
+    }
+
+    public function testStaticCreate()
+    {
+        $formatter = IntlDateFormatter::create('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT);
+        $this->assertInstanceOf('\Symfony\Component\Intl\DateFormatter\IntlDateFormatter', $formatter);
+    }
+
+    public function testFormatWithUnsupportedTimestampArgument()
+    {
+        $formatter = $this->getDefaultDateFormatter();
+
+        $localtime = array(
+            'tm_sec'   => 59,
+            'tm_min'   => 3,
+            'tm_hour'  => 15,
+            'tm_mday'  => 15,
+            'tm_mon'   => 3,
+            'tm_year'  => 112,
+            'tm_wday'  => 0,
+            'tm_yday'  => 105,
+            'tm_isdst' => 0
+        );
+
+        try {
+            $formatter->format($localtime);
+        } catch (\Exception $e) {
+            $this->assertInstanceOf('Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException', $e);
+
+            if (version_compare(PHP_VERSION, '5.3.4', '>=')) {
+                $this->assertStringEndsWith('Only integer unix timestamps and DateTime objects are supported.  Please install the "intl" extension for full localization capabilities.', $e->getMessage());
+            } else {
+                $this->assertStringEndsWith('Only integer unix timestamps are supported.  Please install the "intl" extension for full localization capabilities.', $e->getMessage());
+            }
+        }
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\NotImplementedException
+     */
+    public function testFormatWithUnimplementedChars()
+    {
+        $pattern = 'Y';
+        $formatter = new IntlDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT, 'UTC', IntlDateFormatter::GREGORIAN, $pattern);
+        $formatter->format(0);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\NotImplementedException
+     */
+    public function testFormatWithNonIntegerTimestamp()
+    {
+        $formatter = $this->getDefaultDateFormatter();
+        $formatter->format(array());
+    }
+
+    public function testGetErrorCode()
+    {
+        $formatter = $this->getDefaultDateFormatter();
+        $this->assertEquals(IntlGlobals::getErrorCode(), $formatter->getErrorCode());
+    }
+
+    public function testGetErrorMessage()
+    {
+        $formatter = $this->getDefaultDateFormatter();
+        $this->assertEquals(IntlGlobals::getErrorMessage(), $formatter->getErrorMessage());
+    }
+
+    public function testIsLenient()
+    {
+        $formatter = $this->getDefaultDateFormatter();
+        $this->assertFalse($formatter->isLenient());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testLocaltime()
+    {
+        $formatter = $this->getDefaultDateFormatter();
+        $formatter->localtime('Wednesday, December 31, 1969 4:00:00 PM PT');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodArgumentNotImplementedException
+     */
+    public function testParseWithNotNullPositionValue()
+    {
+        $position = 0;
+        $formatter = $this->getDefaultDateFormatter('y');
+        $this->assertSame(0, $formatter->parse('1970', $position));
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testSetCalendar()
+    {
+        $formatter = $this->getDefaultDateFormatter();
+        $formatter->setCalendar(IntlDateFormatter::GREGORIAN);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException
+     */
+    public function testSetLenient()
+    {
+        $formatter = $this->getDefaultDateFormatter();
+        $formatter->setLenient(true);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\NotImplementedException
+     */
+    public function testFormatWithGmtTimeZoneAndMinutesOffset()
+    {
+        parent::testFormatWithGmtTimeZoneAndMinutesOffset();
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\NotImplementedException
+     */
+    public function testFormatWithNonStandardTimezone()
+    {
+        parent::testFormatWithNonStandardTimezone();
+    }
+
+    public function parseStandaloneAmPmProvider()
+    {
+        return $this->notImplemented(parent::parseStandaloneAmPmProvider());
+    }
+
+    public function parseDayOfWeekProvider()
+    {
+        return $this->notImplemented(parent::parseDayOfWeekProvider());
+    }
+
+    public function parseDayOfYearProvider()
+    {
+        return $this->notImplemented(parent::parseDayOfYearProvider());
+    }
+
+    public function parseQuarterProvider()
+    {
+        return $this->notImplemented(parent::parseQuarterProvider());
+    }
+
+    protected function getDateFormatter($locale, $datetype, $timetype, $timezone = null, $calendar = IntlDateFormatter::GREGORIAN, $pattern = null)
+    {
+        return new IntlDateFormatter($locale, $datetype, $timetype, $timezone, $calendar, $pattern);
+    }
+
+    protected function getIntlErrorMessage()
+    {
+        return IntlGlobals::getErrorMessage();
+    }
+
+    protected function getIntlErrorCode()
+    {
+        return IntlGlobals::getErrorCode();
+    }
+
+    protected function isIntlFailure($errorCode)
+    {
+        return IntlGlobals::isFailure($errorCode);
+    }
+
+    /**
+     * Just to document the differences between the stub and the intl
+     * implementations. The intl can parse any of the tested formats alone. The
+     * stub does not implement them as it would be needed to add more
+     * abstraction, passing more context to the transformers objects. Any of the
+     * formats are ignored alone or with date/time data (years, months, days,
+     * hours, minutes and seconds).
+     *
+     * Also in intl, format like 'ss E' for '10 2' (2nd day of year
+     * + 10 seconds) are added, then we have 86,400 seconds (24h * 60min * 60s)
+     * + 10 seconds
+     *
+     * @param array $dataSets
+     *
+     * @return array
+     */
+    private function notImplemented(array $dataSets)
+    {
+        return array_map(function ($row) {
+                return array($row[0], $row[1], 0);
+            }, $dataSets);
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/DateFormatter/Verification/IntlDateFormatterTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/DateFormatter/Verification/IntlDateFormatterTest.php
new file mode 100644 (file)
index 0000000..7fbf854
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\DateFormatter\Verification;
+
+use Symfony\Component\Intl\DateFormatter\IntlDateFormatter;
+use Symfony\Component\Intl\Tests\DateFormatter\AbstractIntlDateFormatterTest;
+use Symfony\Component\Intl\Util\IntlTestHelper;
+
+/**
+ * Verifies that {@link AbstractIntlDateFormatterTest} matches the behavior of
+ * the {@link \IntlDateFormatter} class in a specific version of ICU.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class IntlDateFormatterTest extends AbstractIntlDateFormatterTest
+{
+    protected function setUp()
+    {
+        IntlTestHelper::requireFullIntl($this);
+
+        parent::setUp();
+    }
+
+    /**
+     * It seems IntlDateFormatter caches the timezone id when not explicitly set via constructor or by the
+     * setTimeZoneId() method. Since testFormatWithDefaultTimezoneIntl() runs using the default environment
+     * time zone, this test would use it too if not running in a separated process.
+     *
+     * @runInSeparateProcess
+     */
+    public function testFormatWithTimezoneFromEnvironmentVariable()
+    {
+        parent::testFormatWithTimezoneFromEnvironmentVariable();
+    }
+
+    protected function getDateFormatter($locale, $datetype, $timetype, $timezone = null, $calendar = IntlDateFormatter::GREGORIAN, $pattern = null)
+    {
+        return new \IntlDateFormatter($locale, $datetype, $timetype, $timezone, $calendar, $pattern);
+    }
+
+    protected function getIntlErrorMessage()
+    {
+        return intl_get_error_message();
+    }
+
+    protected function getIntlErrorCode()
+    {
+        return intl_get_error_code();
+    }
+
+    protected function isIntlFailure($errorCode)
+    {
+        return intl_is_failure($errorCode);
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/Globals/AbstractIntlGlobalsTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/Globals/AbstractIntlGlobalsTest.php
new file mode 100644 (file)
index 0000000..0a937d5
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\Globals;
+
+/**
+ * Test case for intl function implementations.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+abstract class AbstractIntlGlobalsTest extends \PHPUnit_Framework_TestCase
+{
+    public function errorNameProvider()
+    {
+        return array (
+            array(-129, '[BOGUS UErrorCode]'),
+            array(0, 'U_ZERO_ERROR'),
+            array(1, 'U_ILLEGAL_ARGUMENT_ERROR'),
+            array(9, 'U_PARSE_ERROR'),
+            array(129, '[BOGUS UErrorCode]'),
+        );
+    }
+
+    /**
+     * @dataProvider errorNameProvider
+     */
+    public function testGetErrorName($errorCode, $errorName)
+    {
+        $this->assertSame($errorName, $this->getIntlErrorName($errorCode));
+    }
+
+    abstract protected function getIntlErrorName($errorCode);
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/Globals/IntlGlobalsTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/Globals/IntlGlobalsTest.php
new file mode 100644 (file)
index 0000000..34e3a6a
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\Globals;
+
+use Symfony\Component\Intl\Globals\IntlGlobals;
+
+class IntlGlobalsTest extends AbstractIntlGlobalsTest
+{
+    protected function getIntlErrorName($errorCode)
+    {
+        return IntlGlobals::getErrorName($errorCode);
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/Globals/Verification/IntlGlobalsTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/Globals/Verification/IntlGlobalsTest.php
new file mode 100644 (file)
index 0000000..c46033d
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\Globals\Verification;
+
+use Symfony\Component\Intl\Tests\Globals\AbstractIntlGlobalsTest;
+use Symfony\Component\Intl\Util\IntlTestHelper;
+
+/**
+ * Verifies that {@link AbstractIntlGlobalsTest} matches the behavior of the
+ * intl functions with a specific version of ICU.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class IntlGlobalsTest extends AbstractIntlGlobalsTest
+{
+    protected function setUp()
+    {
+        IntlTestHelper::requireFullIntl($this);
+
+        parent::setUp();
+    }
+
+    protected function getIntlErrorName($errorCode)
+    {
+        return intl_error_name($errorCode);
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/Locale/AbstractLocaleTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/Locale/AbstractLocaleTest.php
new file mode 100644 (file)
index 0000000..08569fa
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\Locale;
+
+/**
+ * Test case for Locale implementations.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+abstract class AbstractLocaleTest extends \PHPUnit_Framework_TestCase
+{
+    public function testSetDefault()
+    {
+        $this->call('setDefault', 'en_GB');
+
+        $this->assertSame('en_GB', $this->call('getDefault'));
+    }
+
+    abstract protected function call($methodName);
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/Locale/LocaleTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/Locale/LocaleTest.php
new file mode 100644 (file)
index 0000000..7e95718
--- /dev/null
@@ -0,0 +1,159 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\Locale;
+
+class LocaleTest extends AbstractLocaleTest
+{
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testAcceptFromHttp()
+    {
+        $this->call('acceptFromHttp', 'pt-br,en-us;q=0.7,en;q=0.5');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testComposeLocale()
+    {
+        $subtags = array(
+            'language' => 'pt',
+            'script'   => 'Latn',
+            'region'   => 'BR'
+        );
+        $this->call('composeLocale', $subtags);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testFilterMatches()
+    {
+        $this->call('filterMatches', 'pt-BR', 'pt-BR');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testGetAllVariants()
+    {
+        $this->call('getAllVariants', 'pt_BR_Latn');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testGetDisplayLanguage()
+    {
+        $this->call('getDisplayLanguage', 'pt-Latn-BR', 'en');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testGetDisplayName()
+    {
+        $this->call('getDisplayName', 'pt-Latn-BR', 'en');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testGetDisplayRegion()
+    {
+        $this->call('getDisplayRegion', 'pt-Latn-BR', 'en');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testGetDisplayScript()
+    {
+        $this->call('getDisplayScript', 'pt-Latn-BR', 'en');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testGetDisplayVariant()
+    {
+        $this->call('getDisplayVariant', 'pt-Latn-BR', 'en');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testGetKeywords()
+    {
+        $this->call('getKeywords', 'pt-BR@currency=BRL');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testGetPrimaryLanguage()
+    {
+        $this->call('getPrimaryLanguage', 'pt-Latn-BR');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testGetRegion()
+    {
+        $this->call('getRegion', 'pt-Latn-BR');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testGetScript()
+    {
+        $this->call('getScript', 'pt-Latn-BR');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testLookup()
+    {
+        $langtag = array(
+            'pt-Latn-BR',
+            'pt-BR'
+        );
+        $this->call('lookup', $langtag, 'pt-BR-x-priv1');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testParseLocale()
+    {
+        $this->call('parseLocale', 'pt-Latn-BR');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testSetDefault()
+    {
+        $this->call('setDefault', 'pt_BR');
+    }
+
+    protected function call($methodName)
+    {
+        $args = array_slice(func_get_args(), 1);
+
+        return call_user_func_array(array('Symfony\Component\Intl\Locale\Locale', $methodName), $args);
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/Locale/Verification/LocaleTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/Locale/Verification/LocaleTest.php
new file mode 100644 (file)
index 0000000..39d4f3c
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\Locale\Verification;
+
+use Symfony\Component\Intl\Tests\Locale\AbstractLocaleTest;
+use Symfony\Component\Intl\Util\IntlTestHelper;
+
+/**
+ * Verifies that {@link AbstractLocaleTest} matches the behavior of the
+ * {@link Locale} class with a specific version of ICU.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class LocaleTest extends AbstractLocaleTest
+{
+    protected function setUp()
+    {
+        IntlTestHelper::requireFullIntl($this);
+
+        parent::setUp();
+    }
+
+    protected function call($methodName)
+    {
+        $args = array_slice(func_get_args(), 1);
+
+        return call_user_func_array(array('Locale', $methodName), $args);
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/NumberFormatter/AbstractNumberFormatterTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/NumberFormatter/AbstractNumberFormatterTest.php
new file mode 100644 (file)
index 0000000..ad5581b
--- /dev/null
@@ -0,0 +1,707 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\NumberFormatter;
+
+use Symfony\Component\Intl\Globals\IntlGlobals;
+use Symfony\Component\Intl\Intl;
+use Symfony\Component\Intl\Locale;
+use Symfony\Component\Intl\NumberFormatter\NumberFormatter;
+use Symfony\Component\Intl\Util\IntlTestHelper;
+
+/**
+ * Note that there are some values written like -2147483647 - 1. This is the lower 32bit int max and is a known
+ * behavior of PHP.
+ */
+abstract class AbstractNumberFormatterTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @dataProvider formatCurrencyWithDecimalStyleProvider
+     */
+    public function testFormatCurrencyWithDecimalStyle($value, $currency, $expected)
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+        $this->assertEquals($expected, $formatter->formatCurrency($value, $currency));
+    }
+
+    public function formatCurrencyWithDecimalStyleProvider()
+    {
+        return array(
+            array(100, 'ALL', '100'),
+            array(100, 'BRL', '100.00'),
+            array(100, 'CRC', '100'),
+            array(100, 'JPY', '100'),
+            array(100, 'CHF', '100'),
+            array(-100, 'ALL', '-100'),
+            array(-100, 'BRL', '-100'),
+            array(-100, 'CRC', '-100'),
+            array(-100, 'JPY', '-100'),
+            array(-100, 'CHF', '-100'),
+            array(1000.12, 'ALL', '1,000.12'),
+            array(1000.12, 'BRL', '1,000.12'),
+            array(1000.12, 'CRC', '1,000.12'),
+            array(1000.12, 'JPY', '1,000.12'),
+            array(1000.12, 'CHF', '1,000.12')
+        );
+    }
+
+    /**
+     * @dataProvider formatCurrencyWithCurrencyStyleProvider
+     */
+    public function testFormatCurrencyWithCurrencyStyle($value, $currency, $expected)
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
+        $this->assertEquals($expected, $formatter->formatCurrency($value, $currency));
+    }
+
+    public function formatCurrencyWithCurrencyStyleProvider()
+    {
+        return array(
+            array(100, 'ALL', 'ALL100'),
+            array(-100, 'ALL', '(ALL100)'),
+            array(1000.12, 'ALL', 'ALL1,000'),
+
+            array(100, 'JPY', '¥100'),
+            array(-100, 'JPY', '(¥100)'),
+            array(1000.12, 'JPY', '¥1,000'),
+
+            array(100, 'EUR', '€100.00'),
+            array(-100, 'EUR', '(€100.00)'),
+            array(1000.12, 'EUR', '€1,000.12'),
+        );
+    }
+
+    /**
+     * @dataProvider formatCurrencyWithCurrencyStyleCostaRicanColonsRoundingProvider
+     */
+    public function testFormatCurrencyWithCurrencyStyleCostaRicanColonsRounding($value, $currency, $symbol, $expected)
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
+        $this->assertEquals(sprintf($expected, $symbol), $formatter->formatCurrency($value, $currency));
+    }
+
+    public function formatCurrencyWithCurrencyStyleCostaRicanColonsRoundingProvider()
+    {
+        return array(
+            array(100, 'CRC', 'CRC', '%s100'),
+            array(-100, 'CRC', 'CRC', '(%s100)'),
+            array(1000.12, 'CRC', 'CRC', '%s1,000'),
+        );
+    }
+
+    /**
+     * @dataProvider formatCurrencyWithCurrencyStyleBrazilianRealRoundingProvider
+     */
+    public function testFormatCurrencyWithCurrencyStyleBrazilianRealRounding($value, $currency, $symbol, $expected)
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
+        $this->assertEquals(sprintf($expected, $symbol), $formatter->formatCurrency($value, $currency));
+    }
+
+    public function formatCurrencyWithCurrencyStyleBrazilianRealRoundingProvider()
+    {
+        return array(
+            array(100, 'BRL', 'R', '%s$100.00'),
+            array(-100, 'BRL', 'R', '(%s$100.00)'),
+            array(1000.12, 'BRL', 'R', '%s$1,000.12'),
+
+            // Rounding checks
+            array(1000.121, 'BRL', 'R', '%s$1,000.12'),
+            array(1000.123, 'BRL', 'R', '%s$1,000.12'),
+            array(1000.125, 'BRL', 'R', '%s$1,000.12'),
+            array(1000.127, 'BRL', 'R', '%s$1,000.13'),
+            array(1000.129, 'BRL', 'R', '%s$1,000.13'),
+            array(11.50999, 'BRL', 'R', '%s$11.51'),
+            array(11.9999464, 'BRL', 'R', '%s$12.00')
+        );
+    }
+
+    /**
+     * @dataProvider formatCurrencyWithCurrencyStyleSwissRoundingProvider
+     */
+    public function testFormatCurrencyWithCurrencyStyleSwissRounding($value, $currency, $symbol, $expected)
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
+        $this->assertEquals(sprintf($expected, $symbol), $formatter->formatCurrency($value, $currency));
+    }
+
+    public function formatCurrencyWithCurrencyStyleSwissRoundingProvider()
+    {
+        return array(
+            array(100, 'CHF', 'CHF', '%s100.00'),
+            array(-100, 'CHF', 'CHF', '(%s100.00)'),
+            array(1000.12, 'CHF', 'CHF', '%s1,000.10'),
+            array('1000.12', 'CHF', 'CHF', '%s1,000.10'),
+
+            // Rounding checks
+            array(1000.121, 'CHF', 'CHF', '%s1,000.10'),
+            array(1000.123, 'CHF', 'CHF', '%s1,000.10'),
+            array(1000.125, 'CHF', 'CHF', '%s1,000.10'),
+            array(1000.127, 'CHF', 'CHF', '%s1,000.15'),
+            array(1000.129, 'CHF', 'CHF', '%s1,000.15'),
+
+            array(1200000.00, 'CHF', 'CHF', '%s1,200,000.00'),
+            array(1200000.1, 'CHF', 'CHF', '%s1,200,000.10'),
+            array(1200000.10, 'CHF', 'CHF', '%s1,200,000.10'),
+            array(1200000.101, 'CHF', 'CHF', '%s1,200,000.10')
+        );
+    }
+
+    public function testFormat()
+    {
+        $errorCode = IntlGlobals::U_ZERO_ERROR;
+        $errorMessage = 'U_ZERO_ERROR';
+
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+        $this->assertSame('9.555', $formatter->format(9.555));
+
+        $this->assertSame($errorMessage, $this->getIntlErrorMessage());
+        $this->assertSame($errorCode, $this->getIntlErrorCode());
+        $this->assertFalse($this->isIntlFailure($this->getIntlErrorCode()));
+        $this->assertSame($errorMessage, $formatter->getErrorMessage());
+        $this->assertSame($errorCode, $formatter->getErrorCode());
+        $this->assertFalse($this->isIntlFailure($formatter->getErrorCode()));
+    }
+
+    public function testFormatWithCurrencyStyle()
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
+        $this->assertEquals('¤1.00', $formatter->format(1));
+    }
+
+    /**
+     * @dataProvider formatTypeInt32Provider
+     */
+    public function testFormatTypeInt32($formatter, $value, $expected, $message = '')
+    {
+        $formattedValue = $formatter->format($value, NumberFormatter::TYPE_INT32);
+        $this->assertEquals($expected, $formattedValue, $message);
+    }
+
+    public function formatTypeInt32Provider()
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+
+        $message = '->format() TYPE_INT32 formats inconsistently an integer if out of the 32 bit range.';
+
+        return array(
+            array($formatter, 1, '1'),
+            array($formatter, 1.1, '1'),
+            array($formatter, 2147483648, '-2,147,483,648', $message),
+            array($formatter, -2147483649, '2,147,483,647', $message),
+        );
+    }
+
+    /**
+     * @dataProvider formatTypeInt32WithCurrencyStyleProvider
+     */
+    public function testFormatTypeInt32WithCurrencyStyle($formatter, $value, $expected, $message = '')
+    {
+        $formattedValue = $formatter->format($value, NumberFormatter::TYPE_INT32);
+        $this->assertEquals($expected, $formattedValue, $message);
+    }
+
+    public function formatTypeInt32WithCurrencyStyleProvider()
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
+
+        $message = '->format() TYPE_INT32 formats inconsistently an integer if out of the 32 bit range.';
+
+        return array(
+            array($formatter, 1, '¤1.00'),
+            array($formatter, 1.1, '¤1.00'),
+            array($formatter, 2147483648, '(¤2,147,483,648.00)', $message),
+            array($formatter, -2147483649, '¤2,147,483,647.00', $message)
+        );
+    }
+
+    /**
+     * The parse() method works differently with integer out of the 32 bit range. format() works fine.
+     * @dataProvider formatTypeInt64Provider
+     */
+    public function testFormatTypeInt64($formatter, $value, $expected)
+    {
+        $formattedValue = $formatter->format($value, NumberFormatter::TYPE_INT64);
+        $this->assertEquals($expected, $formattedValue);
+    }
+
+    public function formatTypeInt64Provider()
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+
+        return array(
+            array($formatter, 1, '1'),
+            array($formatter, 1.1, '1'),
+            array($formatter, 2147483648, '2,147,483,648'),
+            array($formatter, -2147483649, '-2,147,483,649'),
+        );
+    }
+
+    /**
+     * @dataProvider formatTypeInt64WithCurrencyStyleProvider
+     */
+    public function testFormatTypeInt64WithCurrencyStyle($formatter, $value, $expected)
+    {
+        $formattedValue = $formatter->format($value, NumberFormatter::TYPE_INT64);
+        $this->assertEquals($expected, $formattedValue);
+    }
+
+    public function formatTypeInt64WithCurrencyStyleProvider()
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
+
+        return array(
+            array($formatter, 1, '¤1.00'),
+            array($formatter, 1.1, '¤1.00'),
+            array($formatter, 2147483648, '¤2,147,483,648.00'),
+            array($formatter, -2147483649, '(¤2,147,483,649.00)')
+        );
+    }
+
+    /**
+     * @dataProvider formatTypeDoubleProvider
+     */
+    public function testFormatTypeDouble($formatter, $value, $expected)
+    {
+        $formattedValue = $formatter->format($value, NumberFormatter::TYPE_DOUBLE);
+        $this->assertEquals($expected, $formattedValue);
+    }
+
+    public function formatTypeDoubleProvider()
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+
+        return array(
+            array($formatter, 1, '1'),
+            array($formatter, 1.1, '1.1'),
+        );
+    }
+
+    /**
+     * @dataProvider formatTypeDoubleWithCurrencyStyleProvider
+     */
+    public function testFormatTypeDoubleWithCurrencyStyle($formatter, $value, $expected)
+    {
+        $formattedValue = $formatter->format($value, NumberFormatter::TYPE_DOUBLE);
+        $this->assertEquals($expected, $formattedValue);
+    }
+
+    public function formatTypeDoubleWithCurrencyStyleProvider()
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
+
+        return array(
+            array($formatter, 1, '¤1.00'),
+            array($formatter, 1.1, '¤1.10'),
+        );
+    }
+
+    /**
+     * @dataProvider formatTypeCurrencyProvider
+     * @expectedException \PHPUnit_Framework_Error_Warning
+     */
+    public function testFormatTypeCurrency($formatter, $value)
+    {
+        $formatter->format($value, NumberFormatter::TYPE_CURRENCY);
+    }
+
+    /**
+     * @dataProvider formatTypeCurrencyProvider
+     */
+    public function testFormatTypeCurrencyReturn($formatter, $value)
+    {
+        $this->assertFalse(@$formatter->format($value, NumberFormatter::TYPE_CURRENCY));
+    }
+
+    public function formatTypeCurrencyProvider()
+    {
+        $df = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+        $cf = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
+
+        return array(
+            array($df, 1),
+            array($cf, 1),
+        );
+    }
+
+    /**
+     * @dataProvider formatFractionDigitsProvider
+     */
+    public function testFormatFractionDigits($value, $expected, $fractionDigits = null, $expectedFractionDigits = 1)
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+
+        if (null !== $fractionDigits) {
+            $attributeRet = $formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, $fractionDigits);
+        }
+
+        $formattedValue = $formatter->format($value);
+        $this->assertSame($expected, $formattedValue);
+        $this->assertSame($expectedFractionDigits, $formatter->getAttribute(NumberFormatter::FRACTION_DIGITS));
+
+        if (isset($attributeRet)) {
+            $this->assertTrue($attributeRet);
+        }
+    }
+
+    public function formatFractionDigitsProvider()
+    {
+        return array(
+            array(1.123, '1.123', null, 0),
+            array(1.123, '1', 0, 0),
+            array(1.123, '1.1', 1, 1),
+            array(1.123, '1.12', 2, 2),
+            array(1.123, '1', -1, 0),
+            array(1.123, '1', 'abc', 0)
+        );
+    }
+
+    /**
+     * @dataProvider formatGroupingUsedProvider
+     */
+    public function testFormatGroupingUsed($value, $expected, $groupingUsed = null, $expectedGroupingUsed = 1)
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+
+        if (null !== $groupingUsed) {
+            $attributeRet = $formatter->setAttribute(NumberFormatter::GROUPING_USED, $groupingUsed);
+        }
+
+        $formattedValue = $formatter->format($value);
+        $this->assertSame($expected, $formattedValue);
+        $this->assertSame($expectedGroupingUsed, $formatter->getAttribute(NumberFormatter::GROUPING_USED));
+
+        if (isset($attributeRet)) {
+            $this->assertTrue($attributeRet);
+        }
+    }
+
+    public function formatGroupingUsedProvider()
+    {
+        return array(
+            array(1000, '1,000', null, 1),
+            array(1000, '1000', 0, 0),
+            array(1000, '1,000', 1, 1),
+            array(1000, '1,000', 2, 1),
+            array(1000, '1000', 'abc', 0),
+            array(1000, '1,000', -1, 1),
+        );
+    }
+
+    /**
+     * @dataProvider formatRoundingModeRoundHalfUpProvider
+     */
+    public function testFormatRoundingModeHalfUp($value, $expected)
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+        $formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, 2);
+
+        $formatter->setAttribute(NumberFormatter::ROUNDING_MODE, NumberFormatter::ROUND_HALFUP);
+        $this->assertSame($expected, $formatter->format($value), '->format() with ROUND_HALFUP rounding mode.');
+    }
+
+    public function formatRoundingModeRoundHalfUpProvider()
+    {
+        // The commented value is differently rounded by intl's NumberFormatter in 32 and 64 bit architectures
+        return array(
+            array(1.121, '1.12'),
+            array(1.123, '1.12'),
+            // array(1.125, '1.13'),
+            array(1.127, '1.13'),
+            array(1.129, '1.13'),
+        );
+    }
+
+    /**
+     * @dataProvider formatRoundingModeRoundHalfDownProvider
+     */
+    public function testFormatRoundingModeHalfDown($value, $expected)
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+        $formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, 2);
+
+        $formatter->setAttribute(NumberFormatter::ROUNDING_MODE, NumberFormatter::ROUND_HALFDOWN);
+        $this->assertSame($expected, $formatter->format($value), '->format() with ROUND_HALFDOWN rounding mode.');
+    }
+
+    public function formatRoundingModeRoundHalfDownProvider()
+    {
+        return array(
+            array(1.121, '1.12'),
+            array(1.123, '1.12'),
+            array(1.125, '1.12'),
+            array(1.127, '1.13'),
+            array(1.129, '1.13'),
+        );
+    }
+
+    /**
+     * @dataProvider formatRoundingModeRoundHalfEvenProvider
+     */
+    public function testFormatRoundingModeHalfEven($value, $expected)
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+        $formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, 2);
+
+        $formatter->setAttribute(NumberFormatter::ROUNDING_MODE, NumberFormatter::ROUND_HALFEVEN);
+        $this->assertSame($expected, $formatter->format($value), '->format() with ROUND_HALFEVEN rounding mode.');
+    }
+
+    public function formatRoundingModeRoundHalfEvenProvider()
+    {
+        return array(
+            array(1.121, '1.12'),
+            array(1.123, '1.12'),
+            array(1.125, '1.12'),
+            array(1.127, '1.13'),
+            array(1.129, '1.13'),
+        );
+    }
+
+    public function testGetLocale()
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+        $this->assertEquals('en', $formatter->getLocale());
+    }
+
+    /**
+     * @dataProvider parseProvider
+     */
+    public function testParse($value, $expected, $message = '')
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+        $parsedValue = $formatter->parse($value, NumberFormatter::TYPE_DOUBLE);
+        $this->assertSame($expected, $parsedValue, $message);
+
+        if ($expected === false) {
+            $errorCode = IntlGlobals::U_PARSE_ERROR;
+            $errorMessage = 'Number parsing failed: U_PARSE_ERROR';
+        } else {
+            $errorCode = IntlGlobals::U_ZERO_ERROR;
+            $errorMessage = 'U_ZERO_ERROR';
+        }
+
+        $this->assertSame($errorMessage, $this->getIntlErrorMessage());
+        $this->assertSame($errorCode, $this->getIntlErrorCode());
+        $this->assertSame($errorCode !== 0, $this->isIntlFailure($this->getIntlErrorCode()));
+        $this->assertSame($errorMessage, $formatter->getErrorMessage());
+        $this->assertSame($errorCode, $formatter->getErrorCode());
+        $this->assertSame($errorCode !== 0, $this->isIntlFailure($formatter->getErrorCode()));
+    }
+
+    public function parseProvider()
+    {
+        return array(
+            array('prefix1', false, '->parse() does not parse a number with a string prefix.'),
+            array('1suffix', (float) 1, '->parse() parses a number with a string suffix.'),
+        );
+    }
+
+    /**
+     * @expectedException \PHPUnit_Framework_Error_Warning
+     */
+    public function testParseTypeDefault()
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+        $formatter->parse('1', NumberFormatter::TYPE_DEFAULT);
+    }
+
+    /**
+     * @dataProvider parseTypeInt32Provider
+     */
+    public function testParseTypeInt32($value, $expected, $message = '')
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+        $parsedValue = $formatter->parse($value, NumberFormatter::TYPE_INT32);
+        $this->assertSame($expected, $parsedValue);
+    }
+
+    public function parseTypeInt32Provider()
+    {
+        return array(
+            array('1', 1),
+            array('1.1', 1),
+            array('2,147,483,647', 2147483647),
+            array('-2,147,483,648', -2147483647 - 1),
+            array('2,147,483,648', false, '->parse() TYPE_INT32 returns false when the number is greater than the integer positive range.'),
+            array('-2,147,483,649', false, '->parse() TYPE_INT32 returns false when the number is greater than the integer negative range.')
+        );
+    }
+
+    public function testParseTypeInt64With32BitIntegerInPhp32Bit()
+    {
+        IntlTestHelper::require32Bit($this);
+
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+
+        $parsedValue = $formatter->parse('2,147,483,647', NumberFormatter::TYPE_INT64);
+        $this->assertInternalType('integer', $parsedValue);
+        $this->assertEquals(2147483647, $parsedValue);
+
+        $parsedValue = $formatter->parse('-2,147,483,648', NumberFormatter::TYPE_INT64);
+
+        // Bug #59597 was fixed on PHP 5.3.14 and 5.4.4
+        // The negative PHP_INT_MAX was being converted to float
+        if (
+            (version_compare(PHP_VERSION, '5.4.0', '<') && version_compare(PHP_VERSION, '5.3.14', '>=')) ||
+            version_compare(PHP_VERSION, '5.4.4', '>=')
+        ) {
+            $this->assertInternalType('int', $parsedValue);
+        } else {
+            $this->assertInternalType('float', $parsedValue);
+        }
+
+        $this->assertEquals(-2147483648, $parsedValue);
+    }
+
+    public function testParseTypeInt64With32BitIntegerInPhp64Bit()
+    {
+        IntlTestHelper::require64Bit($this);
+
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+
+        $parsedValue = $formatter->parse('2,147,483,647', NumberFormatter::TYPE_INT64);
+        $this->assertInternalType('integer', $parsedValue);
+        $this->assertEquals(2147483647, $parsedValue);
+
+        $parsedValue = $formatter->parse('-2,147,483,648', NumberFormatter::TYPE_INT64);
+        $this->assertInternalType('integer', $parsedValue);
+        $this->assertEquals(-2147483647 - 1, $parsedValue);
+    }
+
+    /**
+     * If PHP is compiled in 32bit mode, the returned value for a 64bit integer are float numbers.
+     */
+    public function testParseTypeInt64With64BitIntegerInPhp32Bit()
+    {
+        IntlTestHelper::require32Bit($this);
+
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+
+        // int 64 using only 32 bit range strangeness
+        $parsedValue = $formatter->parse('2,147,483,648', NumberFormatter::TYPE_INT64);
+        $this->assertInternalType('float', $parsedValue);
+        $this->assertEquals(2147483648, $parsedValue, '->parse() TYPE_INT64 does not use true 64 bit integers, using only the 32 bit range.');
+
+        $parsedValue = $formatter->parse('-2,147,483,649', NumberFormatter::TYPE_INT64);
+        $this->assertInternalType('float', $parsedValue);
+        $this->assertEquals(-2147483649, $parsedValue, '->parse() TYPE_INT64 does not use true 64 bit integers, using only the 32 bit range.');
+    }
+
+    /**
+     * If PHP is compiled in 64bit mode, the returned value for a 64bit integer are 32bit integer numbers.
+     */
+    public function testParseTypeInt64With64BitIntegerInPhp64Bit()
+    {
+        IntlTestHelper::require64Bit($this);
+
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+
+        $parsedValue = $formatter->parse('2,147,483,648', NumberFormatter::TYPE_INT64);
+        $this->assertInternalType('integer', $parsedValue);
+
+        // 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 (
+            (version_compare(PHP_VERSION, '5.3.14', '<')) ||
+            (version_compare(PHP_VERSION, '5.4.0', '>=') && version_compare(PHP_VERSION, '5.4.4', '<'))
+        ) {
+            $this->assertEquals(-2147483648, $parsedValue, '->parse() TYPE_INT64 does not use true 64 bit integers, using only the 32 bit range (PHP < 5.3.14 and PHP < 5.4.4).');
+        } else {
+            $this->assertEquals(2147483648, $parsedValue, '->parse() TYPE_INT64 uses true 64 bit integers (PHP >= 5.3.14 and PHP >= 5.4.4).');
+        }
+
+        $parsedValue = $formatter->parse('-2,147,483,649', NumberFormatter::TYPE_INT64);
+        $this->assertInternalType('integer', $parsedValue);
+
+        // 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 (
+            (version_compare(PHP_VERSION, '5.3.14', '<')) ||
+            (version_compare(PHP_VERSION, '5.4.0', '>=') && version_compare(PHP_VERSION, '5.4.4', '<'))
+        ) {
+            $this->assertEquals(2147483647, $parsedValue, '->parse() TYPE_INT64 does not use true 64 bit integers, using only the 32 bit range  (PHP < 5.3.14 and PHP < 5.4.4).');
+        } else {
+            $this->assertEquals(-2147483649, $parsedValue, '->parse() TYPE_INT64 uses true 64 bit integers (PHP >= 5.3.14 and PHP >= 5.4.4).');
+        }
+    }
+
+    /**
+     * @dataProvider parseTypeDoubleProvider
+     */
+    public function testParseTypeDouble($value, $expectedValue)
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+        $parsedValue = $formatter->parse($value, NumberFormatter::TYPE_DOUBLE);
+        $this->assertSame($expectedValue, $parsedValue);
+    }
+
+    public function parseTypeDoubleProvider()
+    {
+        return array(
+            array('1', (float) 1),
+            array('1.1', 1.1),
+            array('9,223,372,036,854,775,808', 9223372036854775808),
+            array('-9,223,372,036,854,775,809', -9223372036854775809),
+        );
+    }
+
+    /**
+     * @expectedException \PHPUnit_Framework_Error_Warning
+     */
+    public function testParseTypeCurrency()
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+        $formatter->parse('1', NumberFormatter::TYPE_CURRENCY);
+    }
+
+    public function testParseWithNullPositionValue()
+    {
+        $position = null;
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+        $formatter->parse('123', NumberFormatter::TYPE_INT32, $position);
+        $this->assertNull($position);
+    }
+
+    public function testParseWithNotNullPositionValue()
+    {
+        $position = 1;
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+        $formatter->parse('123', NumberFormatter::TYPE_DOUBLE, $position);
+        $this->assertEquals(3, $position);
+    }
+
+    /**
+     * @param string $locale
+     * @param null $style
+     * @param null $pattern
+     *
+     * @return \NumberFormatter
+     */
+    abstract protected function getNumberFormatter($locale = 'en', $style = null, $pattern = null);
+
+    /**
+     * @return string
+     */
+    abstract protected function getIntlErrorMessage();
+
+    /**
+     * @return integer
+     */
+    abstract protected function getIntlErrorCode();
+
+    /**
+     * @param integer $errorCode
+     *
+     * @return Boolean
+     */
+    abstract protected function isIntlFailure($errorCode);
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/NumberFormatter/NumberFormatterTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/NumberFormatter/NumberFormatterTest.php
new file mode 100644 (file)
index 0000000..36e8914
--- /dev/null
@@ -0,0 +1,239 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\NumberFormatter;
+
+use Symfony\Component\Intl\Globals\IntlGlobals;
+use Symfony\Component\Intl\NumberFormatter\NumberFormatter;
+use Symfony\Component\Intl\Util\IntlTestHelper;
+
+/**
+ * Note that there are some values written like -2147483647 - 1. This is the lower 32bit int max and is a known
+ * behavior of PHP.
+ */
+class NumberFormatterTest extends AbstractNumberFormatterTest
+{
+    protected function setUp()
+    {
+        IntlTestHelper::requireIntl($this);
+
+        parent::setUp();
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException
+     */
+    public function testConstructorWithUnsupportedLocale()
+    {
+        new NumberFormatter('pt_BR');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException
+     */
+    public function testConstructorWithUnsupportedStyle()
+    {
+        new NumberFormatter('en', NumberFormatter::PATTERN_DECIMAL);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodArgumentNotImplementedException
+     */
+    public function testConstructorWithPatternDifferentThanNull()
+    {
+        new NumberFormatter('en', NumberFormatter::DECIMAL, '');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException
+     */
+    public function testSetAttributeWithUnsupportedAttribute()
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+        $formatter->setAttribute(NumberFormatter::LENIENT_PARSE, null);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException
+     */
+    public function testSetAttributeInvalidRoundingMode()
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+        $formatter->setAttribute(NumberFormatter::ROUNDING_MODE, null);
+    }
+
+    public function testCreate()
+    {
+        $this->assertInstanceOf(
+            '\Symfony\Component\Intl\NumberFormatter\NumberFormatter',
+            NumberFormatter::create('en', NumberFormatter::DECIMAL)
+        );
+    }
+
+    /**
+     * @expectedException \RuntimeException
+     */
+    public function testFormatWithCurrencyStyle()
+    {
+        parent::testFormatWithCurrencyStyle();
+    }
+
+    /**
+     * @dataProvider formatTypeInt32Provider
+     * @expectedException \Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException
+     */
+    public function testFormatTypeInt32($formatter, $value, $expected, $message = '')
+    {
+        parent::testFormatTypeInt32($formatter, $value, $expected, $message);
+    }
+
+    /**
+     * @dataProvider formatTypeInt32WithCurrencyStyleProvider
+     * @expectedException \Symfony\Component\Intl\Exception\NotImplementedException
+     */
+    public function testFormatTypeInt32WithCurrencyStyle($formatter, $value, $expected, $message = '')
+    {
+        parent::testFormatTypeInt32WithCurrencyStyle($formatter, $value, $expected, $message);
+    }
+
+    /**
+     * @dataProvider formatTypeInt64Provider
+     * @expectedException \Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException
+     */
+    public function testFormatTypeInt64($formatter, $value, $expected)
+    {
+        parent::testFormatTypeInt64($formatter, $value, $expected);
+    }
+
+    /**
+     * @dataProvider formatTypeInt64WithCurrencyStyleProvider
+     * @expectedException \Symfony\Component\Intl\Exception\NotImplementedException
+     */
+    public function testFormatTypeInt64WithCurrencyStyle($formatter, $value, $expected)
+    {
+        parent::testFormatTypeInt64WithCurrencyStyle($formatter, $value, $expected);
+    }
+
+    /**
+     * @dataProvider formatTypeDoubleProvider
+     * @expectedException \Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException
+     */
+    public function testFormatTypeDouble($formatter, $value, $expected)
+    {
+        parent::testFormatTypeDouble($formatter, $value, $expected);
+    }
+
+    /**
+     * @dataProvider formatTypeDoubleWithCurrencyStyleProvider
+     * @expectedException \Symfony\Component\Intl\Exception\NotImplementedException
+     */
+    public function testFormatTypeDoubleWithCurrencyStyle($formatter, $value, $expected)
+    {
+        parent::testFormatTypeDoubleWithCurrencyStyle($formatter, $value, $expected);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testGetPattern()
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+        $formatter->getPattern();
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testGetSymbol()
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+        $formatter->getSymbol(null);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testGetTextAttribute()
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+        $formatter->getTextAttribute(null);
+    }
+
+    public function testGetErrorCode()
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+        $this->assertEquals(IntlGlobals::U_ZERO_ERROR, $formatter->getErrorCode());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testParseCurrency()
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+        $formatter->parseCurrency(null, $currency);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodArgumentNotImplementedException
+     */
+    public function testParseWithNotNullPositionValue()
+    {
+        parent::testParseWithNotNullPositionValue();
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testSetPattern()
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+        $formatter->setPattern(null);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testSetSymbol()
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+        $formatter->setSymbol(null, null);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
+     */
+    public function testSetTextAttribute()
+    {
+        $formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
+        $formatter->setTextAttribute(null, null);
+    }
+
+    protected function getNumberFormatter($locale = 'en', $style = null, $pattern = null)
+    {
+        return new NumberFormatter($locale, $style, $pattern);
+    }
+
+    protected function getIntlErrorMessage()
+    {
+        return IntlGlobals::getErrorMessage();
+    }
+
+    protected function getIntlErrorCode()
+    {
+        return IntlGlobals::getErrorCode();
+    }
+
+    protected function isIntlFailure($errorCode)
+    {
+        return IntlGlobals::isFailure($errorCode);
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/NumberFormatter/Verification/NumberFormatterTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/NumberFormatter/Verification/NumberFormatterTest.php
new file mode 100644 (file)
index 0000000..28e6fe9
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\NumberFormatter\Verification;
+
+use Symfony\Component\Intl\Tests\NumberFormatter\AbstractNumberFormatterTest;
+use Symfony\Component\Intl\Util\IntlTestHelper;
+
+/**
+ * Note that there are some values written like -2147483647 - 1. This is the lower 32bit int max and is a known
+ * behavior of PHP.
+ */
+class NumberFormatterTest extends AbstractNumberFormatterTest
+{
+    protected function setUp()
+    {
+        IntlTestHelper::requireFullIntl($this);
+
+        parent::setUp();
+    }
+
+    public function testCreate()
+    {
+        $this->assertInstanceOf('\NumberFormatter', \NumberFormatter::create('en', \NumberFormatter::DECIMAL));
+    }
+
+    protected function getNumberFormatter($locale = 'en', $style = null, $pattern = null)
+    {
+        return new \NumberFormatter($locale, $style, $pattern);
+    }
+
+    protected function getIntlErrorMessage()
+    {
+        return intl_get_error_message();
+    }
+
+    protected function getIntlErrorCode()
+    {
+        return intl_get_error_code();
+    }
+
+    protected function isIntlFailure($errorCode)
+    {
+        return intl_is_failure($errorCode);
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/AbstractBundleTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/AbstractBundleTest.php
new file mode 100644 (file)
index 0000000..6b07586
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\ResourceBundle;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class AbstractBundleTest extends \PHPUnit_Framework_TestCase
+{
+    const RES_DIR = '/base/dirName';
+
+    /**
+     * @var \Symfony\Component\Intl\ResourceBundle\AbstractBundle
+     */
+    private $bundle;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    private $reader;
+
+    protected function setUp()
+    {
+        $this->reader = $this->getMock('Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface');
+        $this->bundle = $this->getMockForAbstractClass(
+            'Symfony\Component\Intl\ResourceBundle\AbstractBundle',
+            array(self::RES_DIR, $this->reader)
+        );
+
+        $this->bundle->expects($this->any())
+            ->method('getDirectoryName')
+            ->will($this->returnValue('dirName'));
+    }
+
+    public function testGetLocales()
+    {
+        $locales = array('de', 'en', 'fr');
+
+        $this->reader->expects($this->once())
+            ->method('getLocales')
+            ->with(self::RES_DIR)
+            ->will($this->returnValue($locales));
+
+        $this->assertSame($locales, $this->bundle->getLocales());
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/CurrencyBundleTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/CurrencyBundleTest.php
new file mode 100644 (file)
index 0000000..b66a672
--- /dev/null
@@ -0,0 +1,98 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\ResourceBundle;
+
+use Symfony\Component\Intl\ResourceBundle\CurrencyBundle;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class CurrencyBundleTest extends \PHPUnit_Framework_TestCase
+{
+    const RES_DIR = '/base/curr';
+
+    /**
+     * @var CurrencyBundle
+     */
+    private $bundle;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    private $reader;
+
+    protected function setUp()
+    {
+        $this->reader = $this->getMock('Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface');
+        $this->bundle = new CurrencyBundle(self::RES_DIR, $this->reader);
+    }
+
+    public function testGetCurrencySymbol()
+    {
+        $this->reader->expects($this->once())
+            ->method('readEntry')
+            ->with(self::RES_DIR, 'en', array('Currencies', 'EUR', 1))
+            ->will($this->returnValue('€'));
+
+        $this->assertSame('€', $this->bundle->getCurrencySymbol('EUR', 'en'));
+    }
+
+    public function testGetCurrencyName()
+    {
+        $this->reader->expects($this->once())
+            ->method('readEntry')
+            ->with(self::RES_DIR, 'en', array('Currencies', 'EUR', 0))
+            ->will($this->returnValue('Euro'));
+
+        $this->assertSame('Euro', $this->bundle->getCurrencyName('EUR', 'en'));
+    }
+
+    public function testGetCurrencyNames()
+    {
+        $sortedCurrencies = array(
+            'USD' => array(0 => 'Dollar'),
+            'EUR' => array(0 => 'Euro'),
+        );
+
+        $this->reader->expects($this->once())
+            ->method('readEntry')
+            ->with(self::RES_DIR, 'en', array('Currencies'))
+            ->will($this->returnValue($sortedCurrencies));
+
+        $sortedNames = array(
+            'USD' => 'Dollar',
+            'EUR' => 'Euro',
+        );
+
+        $this->assertSame($sortedNames, $this->bundle->getCurrencyNames('en'));
+    }
+
+    public function testGetFractionDigits()
+    {
+        $this->reader->expects($this->once())
+            ->method('readEntry')
+            ->with(self::RES_DIR, 'en', array('Currencies', 'EUR', 2))
+            ->will($this->returnValue(123));
+
+        $this->assertSame(123, $this->bundle->getFractionDigits('EUR'));
+    }
+
+    public function testGetRoundingIncrement()
+    {
+        $this->reader->expects($this->once())
+            ->method('readEntry')
+            ->with(self::RES_DIR, 'en', array('Currencies', 'EUR', 3))
+            ->will($this->returnValue(123));
+
+        $this->assertSame(123, $this->bundle->getRoundingIncrement('EUR'));
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/LanguageBundleTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/LanguageBundleTest.php
new file mode 100644 (file)
index 0000000..96031fc
--- /dev/null
@@ -0,0 +1,197 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\ResourceBundle;
+
+use Symfony\Component\Intl\ResourceBundle\LanguageBundle;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class LanguageBundleTest extends \PHPUnit_Framework_TestCase
+{
+    const RES_DIR = '/base/lang';
+
+    /**
+     * @var LanguageBundle
+     */
+    private $bundle;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    private $reader;
+
+    protected function setUp()
+    {
+        $this->reader = $this->getMock('Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface');
+        $this->bundle = new LanguageBundle(self::RES_DIR, $this->reader);
+    }
+
+    public function testGetLanguageName()
+    {
+        $languages = array(
+            'de' => 'German',
+            'en' => 'English',
+        );
+
+        $this->reader->expects($this->once())
+            ->method('readEntry')
+            ->with(self::RES_DIR, 'en', array('Languages'))
+            ->will($this->returnValue($languages));
+
+        $this->assertSame('German', $this->bundle->getLanguageName('de', null, 'en'));
+    }
+
+    public function testGetLanguageNameWithRegion()
+    {
+        $languages = array(
+            'de' => 'German',
+            'en' => 'English',
+            'en_GB' => 'British English',
+        );
+
+        $this->reader->expects($this->once())
+            ->method('readEntry')
+            ->with(self::RES_DIR, 'en', array('Languages'))
+            ->will($this->returnValue($languages));
+
+        $this->assertSame('British English', $this->bundle->getLanguageName('en', 'GB', 'en'));
+    }
+
+    public function testGetLanguageNameWithUntranslatedRegion()
+    {
+        $languages = array(
+            'de' => 'German',
+            'en' => 'English',
+        );
+
+        $this->reader->expects($this->once())
+            ->method('readEntry')
+            ->with(self::RES_DIR, 'en', array('Languages'))
+            ->will($this->returnValue($languages));
+
+        $this->assertSame('English', $this->bundle->getLanguageName('en', 'US', 'en'));
+    }
+
+    public function testGetLanguageNames()
+    {
+        $sortedLanguages = array(
+            'en' => 'English',
+            'de' => 'German',
+        );
+
+        $this->reader->expects($this->once())
+            ->method('readEntry')
+            ->with(self::RES_DIR, 'en', array('Languages'))
+            ->will($this->returnValue($sortedLanguages));
+
+        $this->assertSame($sortedLanguages, $this->bundle->getLanguageNames('en'));
+    }
+
+    public function testGetScriptName()
+    {
+        $data = array(
+            'Languages' => array(
+                'de' => 'German',
+                'en' => 'English',
+            ),
+            'Scripts' => array(
+                'Latn' => 'latin',
+                'Cyrl' => 'cyrillique',
+            ),
+        );
+
+        $this->reader->expects($this->once())
+            ->method('read')
+            ->with(self::RES_DIR, 'en')
+            ->will($this->returnValue($data));
+
+        $this->assertSame('latin', $this->bundle->getScriptName('Latn', null, 'en'));
+    }
+
+    public function testGetScriptNameIncludedInLanguage()
+    {
+        $data = array(
+            'Languages' => array(
+                'de' => 'German',
+                'en' => 'English',
+                'zh_Hans' => 'Simplified Chinese',
+            ),
+            'Scripts' => array(
+                'Latn' => 'latin',
+                'Cyrl' => 'cyrillique',
+            ),
+        );
+
+        $this->reader->expects($this->once())
+            ->method('read')
+            ->with(self::RES_DIR, 'en')
+            ->will($this->returnValue($data));
+
+        // Null because the script is included in the language anyway
+        $this->assertNull($this->bundle->getScriptName('Hans', 'zh', 'en'));
+    }
+
+    public function testGetScriptNameIncludedInLanguageInBraces()
+    {
+        $data = array(
+            'Languages' => array(
+                'de' => 'German',
+                'en' => 'English',
+                'zh_Hans' => 'Chinese (simplified)',
+            ),
+            'Scripts' => array(
+                'Latn' => 'latin',
+                'Cyrl' => 'cyrillique',
+            ),
+        );
+
+        $this->reader->expects($this->once())
+            ->method('read')
+            ->with(self::RES_DIR, 'en')
+            ->will($this->returnValue($data));
+
+        $this->assertSame('simplified', $this->bundle->getScriptName('Hans', 'zh', 'en'));
+    }
+
+    public function testGetScriptNameNoScriptsBlock()
+    {
+        $data = array(
+            'Languages' => array(
+                'de' => 'German',
+                'en' => 'English',
+            ),
+        );
+
+        $this->reader->expects($this->once())
+            ->method('read')
+            ->with(self::RES_DIR, 'en')
+            ->will($this->returnValue($data));
+
+        $this->assertNull($this->bundle->getScriptName('Latn', null, 'en'));
+    }
+
+    public function testGetScriptNames()
+    {
+        $sortedScripts = array(
+            'Cyrl' => 'cyrillique',
+            'Latn' => 'latin',
+        );
+
+        $this->reader->expects($this->once())
+            ->method('readEntry')
+            ->with(self::RES_DIR, 'en', array('Scripts'))
+            ->will($this->returnValue($sortedScripts));
+
+        $this->assertSame($sortedScripts, $this->bundle->getScriptNames('en'));
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/LocaleBundleTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/LocaleBundleTest.php
new file mode 100644 (file)
index 0000000..ddfdc3d
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\ResourceBundle;
+
+use Symfony\Component\Intl\ResourceBundle\LocaleBundle;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class LocaleBundleTest extends \PHPUnit_Framework_TestCase
+{
+    const RES_DIR = '/base/locales';
+
+    /**
+     * @var LocaleBundle
+     */
+    private $bundle;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    private $reader;
+
+    protected function setUp()
+    {
+        $this->reader = $this->getMock('Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface');
+        $this->bundle = new LocaleBundle(self::RES_DIR, $this->reader);
+    }
+
+    public function testGetLocaleName()
+    {
+        $this->reader->expects($this->once())
+            ->method('readEntry')
+            ->with(self::RES_DIR, 'en', array('Locales', 'de_AT'))
+            ->will($this->returnValue('German (Austria)'));
+
+        $this->assertSame('German (Austria)', $this->bundle->getLocaleName('de_AT', 'en'));
+    }
+
+    public function testGetLocaleNames()
+    {
+        $sortedLocales = array(
+            'en_IE' => 'English (Ireland)',
+            'en_GB' => 'English (United Kingdom)',
+            'en_US' => 'English (United States)',
+        );
+
+        $this->reader->expects($this->once())
+            ->method('readEntry')
+            ->with(self::RES_DIR, 'en', array('Locales'))
+            ->will($this->returnValue($sortedLocales));
+
+        $this->assertSame($sortedLocales, $this->bundle->getLocaleNames('en'));
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/AbstractBundleReaderTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/AbstractBundleReaderTest.php
new file mode 100644 (file)
index 0000000..2da7f90
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\ResourceBundle\Reader;
+
+use Symfony\Component\Filesystem\Filesystem;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class AbstractBundleReaderTest extends \PHPUnit_Framework_TestCase
+{
+    private $directory;
+
+    /**
+     * @var Filesystem
+     */
+    private $filesystem;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    private $reader;
+
+    protected function setUp()
+    {
+        $this->directory = sys_get_temp_dir() . '/AbstractBundleReaderTest/' . rand(1000, 9999);
+        $this->filesystem = new Filesystem();
+        $this->reader = $this->getMockForAbstractClass('Symfony\Component\Intl\ResourceBundle\Reader\AbstractBundleReader');
+
+        $this->filesystem->mkdir($this->directory);
+    }
+
+    protected function tearDown()
+    {
+        $this->filesystem->remove($this->directory);
+    }
+
+    public function testGetLocales()
+    {
+        $this->filesystem->touch($this->directory . '/en.foo');
+        $this->filesystem->touch($this->directory . '/de.foo');
+        $this->filesystem->touch($this->directory . '/fr.foo');
+        $this->filesystem->touch($this->directory . '/bo.txt');
+        $this->filesystem->touch($this->directory . '/gu.bin');
+        $this->filesystem->touch($this->directory . '/s.lol');
+
+        $this->reader->expects($this->any())
+            ->method('getFileExtension')
+            ->will($this->returnValue('foo'));
+
+        $sortedLocales = array('de', 'en', 'fr');
+
+        $this->assertSame($sortedLocales, $this->reader->getLocales($this->directory));
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/BinaryBundleReaderTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/BinaryBundleReaderTest.php
new file mode 100644 (file)
index 0000000..3aefbae
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\ResourceBundle\Reader;
+
+use Symfony\Component\Intl\ResourceBundle\Reader\BinaryBundleReader;
+use Symfony\Component\Intl\Util\IntlTestHelper;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class BinaryBundleReaderTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var BinaryBundleReader
+     */
+    private $reader;
+
+    protected function setUp()
+    {
+        IntlTestHelper::requireFullIntl($this);
+
+        $this->reader = new BinaryBundleReader();
+    }
+
+    public function testReadReturnsArrayAccess()
+    {
+        $data = $this->reader->read(__DIR__ . '/Fixtures', 'en');
+
+        $this->assertInstanceOf('\ArrayAccess', $data);
+        $this->assertSame('Bar', $data['Foo']);
+        $this->assertFalse(isset($data['ExistsNot']));
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\RuntimeException
+     */
+    public function testReadFailsIfNonExistingLocale()
+    {
+        $this->reader->read(__DIR__ . '/Fixtures', 'foo');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\RuntimeException
+     */
+    public function testReadFailsIfNonExistingDirectory()
+    {
+        $this->reader->read(__DIR__ . '/foo', 'en');
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/NotAFile/en.php/.gitkeep b/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/NotAFile/en.php/.gitkeep
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/en.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/en.php
new file mode 100644 (file)
index 0000000..f2b06a9
--- /dev/null
@@ -0,0 +1,14 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+    'Foo' => 'Bar',
+);
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/en.res b/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/en.res
new file mode 100644 (file)
index 0000000..c78e904
Binary files /dev/null and b/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/en.res differ
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/en.txt b/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/en.txt
new file mode 100644 (file)
index 0000000..c788e99
--- /dev/null
@@ -0,0 +1,3 @@
+en{
+    Foo{"Bar"}
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/PhpBundleReaderTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/PhpBundleReaderTest.php
new file mode 100644 (file)
index 0000000..2fee355
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\ResourceBundle\Reader;
+
+use Symfony\Component\Intl\ResourceBundle\Reader\PhpBundleReader;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class PhpBundleReaderTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var PhpBundleReader
+     */
+    private $reader;
+
+    protected function setUp()
+    {
+        $this->reader = new PhpBundleReader();
+    }
+
+    public function testReadReturnsArray()
+    {
+        $data = $this->reader->read(__DIR__ . '/Fixtures', 'en');
+
+        $this->assertTrue(is_array($data));
+        $this->assertSame('Bar', $data['Foo']);
+        $this->assertFalse(isset($data['ExistsNot']));
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\InvalidArgumentException
+     */
+    public function testReadFailsIfLocaleOtherThanEn()
+    {
+        $this->reader->read(__DIR__ . '/Fixtures', 'foo');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\RuntimeException
+     */
+    public function testReadFailsIfNonExistingDirectory()
+    {
+        $this->reader->read(__DIR__ . '/foo', 'en');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\RuntimeException
+     */
+    public function testReadFailsIfNotAFile()
+    {
+        $this->reader->read(__DIR__ . '/Fixtures/NotAFile', 'en');
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/StructuredBundleReaderTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/StructuredBundleReaderTest.php
new file mode 100644 (file)
index 0000000..600236e
--- /dev/null
@@ -0,0 +1,223 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\ResourceBundle\Reader;
+
+use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReader;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class StructuredBundleReaderTest extends \PHPUnit_Framework_TestCase
+{
+    const RES_DIR = '/res/dir';
+
+    /**
+     * @var StructuredBundleReader
+     */
+    private $reader;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    private $readerImpl;
+
+    protected function setUp()
+    {
+        $this->readerImpl = $this->getMock('Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface');
+        $this->reader = new StructuredBundleReader($this->readerImpl);
+    }
+
+    public function testGetLocales()
+    {
+        $locales = array('en', 'de', 'fr');
+
+        $this->readerImpl->expects($this->once())
+            ->method('getLocales')
+            ->with(self::RES_DIR)
+            ->will($this->returnValue($locales));
+
+        $this->assertSame($locales, $this->reader->getLocales(self::RES_DIR));
+    }
+
+    public function testRead()
+    {
+        $data = array('foo', 'bar');
+
+        $this->readerImpl->expects($this->once())
+            ->method('read')
+            ->with(self::RES_DIR, 'en')
+            ->will($this->returnValue($data));
+
+        $this->assertSame($data, $this->reader->read(self::RES_DIR, 'en'));
+    }
+
+    public function testReadEntryNoParams()
+    {
+        $data = array('foo', 'bar');
+
+        $this->readerImpl->expects($this->once())
+            ->method('read')
+            ->with(self::RES_DIR, 'en')
+            ->will($this->returnValue($data));
+
+        $this->assertSame($data, $this->reader->readEntry(self::RES_DIR, 'en', array()));
+    }
+
+    public function testReadEntryWithParam()
+    {
+        $data = array('Foo' => array('Bar' => 'Baz'));
+
+        $this->readerImpl->expects($this->once())
+            ->method('read')
+            ->with(self::RES_DIR, 'en')
+            ->will($this->returnValue($data));
+
+        $this->assertSame('Baz', $this->reader->readEntry(self::RES_DIR, 'en', array('Foo', 'Bar')));
+    }
+
+    public function testReadEntryWithUnresolvablePath()
+    {
+        $data = array('Foo' => 'Baz');
+
+        $this->readerImpl->expects($this->once())
+            ->method('read')
+            ->with(self::RES_DIR, 'en')
+            ->will($this->returnValue($data));
+
+        $this->assertNull($this->reader->readEntry(self::RES_DIR, 'en', array('Foo', 'Bar')));
+    }
+
+    public function readMergedEntryProvider()
+    {
+        return array(
+            array('foo', null, 'foo'),
+            array(null, 'foo', 'foo'),
+            array(array('foo', 'bar'), null, array('foo', 'bar')),
+            array(array('foo', 'bar'), array(), array('foo', 'bar')),
+            array(null, array('baz'), array('baz')),
+            array(array(), array('baz'), array('baz')),
+            array(array('foo', 'bar'), array('baz'), array('baz', 'foo', 'bar')),
+        );
+    }
+
+    /**
+     * @dataProvider readMergedEntryProvider
+     */
+    public function testReadMergedEntryNoParams($childData, $parentData, $result)
+    {
+        $this->readerImpl->expects($this->at(0))
+            ->method('read')
+            ->with(self::RES_DIR, 'en_GB')
+            ->will($this->returnValue($childData));
+
+        if (null === $childData || is_array($childData)) {
+            $this->readerImpl->expects($this->at(1))
+                ->method('read')
+                ->with(self::RES_DIR, 'en')
+                ->will($this->returnValue($parentData));
+        }
+
+        $this->assertSame($result, $this->reader->readEntry(self::RES_DIR, 'en_GB', array(), true));
+    }
+
+    /**
+     * @dataProvider readMergedEntryProvider
+     */
+    public function testReadMergedEntryWithParams($childData, $parentData, $result)
+    {
+        $this->readerImpl->expects($this->at(0))
+            ->method('read')
+            ->with(self::RES_DIR, 'en_GB')
+            ->will($this->returnValue(array('Foo' => array('Bar' => $childData))));
+
+        if (null === $childData || is_array($childData)) {
+            $this->readerImpl->expects($this->at(1))
+                ->method('read')
+                ->with(self::RES_DIR, 'en')
+                ->will($this->returnValue(array('Foo' => array('Bar' => $parentData))));
+        }
+
+        $this->assertSame($result, $this->reader->readEntry(self::RES_DIR, 'en_GB', array('Foo', 'Bar'), true));
+    }
+
+    public function testReadMergedEntryWithUnresolvablePath()
+    {
+        $this->readerImpl->expects($this->at(0))
+            ->method('read')
+            ->with(self::RES_DIR, 'en_GB')
+            ->will($this->returnValue(array('Foo' => 'Baz')));
+
+        $this->readerImpl->expects($this->at(1))
+            ->method('read')
+            ->with(self::RES_DIR, 'en')
+            ->will($this->returnValue(array('Foo' => 'Bar')));
+
+        $this->assertNull($this->reader->readEntry(self::RES_DIR, 'en_GB', array('Foo', 'Bar'), true));
+    }
+
+    public function testReadMergedEntryWithUnresolvablePathInParent()
+    {
+        $this->readerImpl->expects($this->at(0))
+            ->method('read')
+            ->with(self::RES_DIR, 'en_GB')
+            ->will($this->returnValue(array('Foo' => array('Bar' => array('three')))));
+
+        $this->readerImpl->expects($this->at(1))
+            ->method('read')
+            ->with(self::RES_DIR, 'en')
+            ->will($this->returnValue(array('Foo' => 'Bar')));
+
+        $result = array('three');
+
+        $this->assertSame($result, $this->reader->readEntry(self::RES_DIR, 'en_GB', array('Foo', 'Bar'), true));
+    }
+
+    public function testReadMergedEntryWithUnresolvablePathInChild()
+    {
+        $this->readerImpl->expects($this->at(0))
+            ->method('read')
+            ->with(self::RES_DIR, 'en_GB')
+            ->will($this->returnValue(array('Foo' => 'Baz')));
+
+        $this->readerImpl->expects($this->at(1))
+            ->method('read')
+            ->with(self::RES_DIR, 'en')
+            ->will($this->returnValue(array('Foo' => array('Bar' => array('one', 'two')))));
+
+        $result = array('one', 'two');
+
+        $this->assertSame($result, $this->reader->readEntry(self::RES_DIR, 'en_GB', array('Foo', 'Bar'), true));
+    }
+
+    /**
+     * @dataProvider readMergedEntryProvider
+     */
+    public function testReadMergedEntryWithTraversables($childData, $parentData, $result)
+    {
+        $parentData = is_array($parentData) ? new \ArrayObject($parentData) : $parentData;
+        $childData = is_array($childData) ? new \ArrayObject($childData) : $childData;
+
+        $this->readerImpl->expects($this->at(0))
+            ->method('read')
+            ->with(self::RES_DIR, 'en_GB')
+            ->will($this->returnValue(array('Foo' => array('Bar' => $childData))));
+
+        if (null === $childData || $childData instanceof \ArrayObject) {
+            $this->readerImpl->expects($this->at(1))
+                ->method('read')
+                ->with(self::RES_DIR, 'en')
+                ->will($this->returnValue(array('Foo' => array('Bar' => $parentData))));
+        }
+
+        $this->assertSame($result, $this->reader->readEntry(self::RES_DIR, 'en_GB', array('Foo', 'Bar'), true));
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/RegionBundleTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/RegionBundleTest.php
new file mode 100644 (file)
index 0000000..ccdf904
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\ResourceBundle;
+
+use Symfony\Component\Intl\ResourceBundle\RegionBundle;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class RegionBundleTest extends \PHPUnit_Framework_TestCase
+{
+    const RES_DIR = '/base/region';
+
+    /**
+     * @var RegionBundle
+     */
+    private $bundle;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    private $reader;
+
+    protected function setUp()
+    {
+        $this->reader = $this->getMock('Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface');
+        $this->bundle = new RegionBundle(self::RES_DIR, $this->reader);
+    }
+
+    public function testGetCountryName()
+    {
+        $this->reader->expects($this->once())
+            ->method('readEntry')
+            ->with(self::RES_DIR, 'en', array('Countries', 'AT'))
+            ->will($this->returnValue('Austria'));
+
+        $this->assertSame('Austria', $this->bundle->getCountryName('AT', 'en'));
+    }
+
+    public function testGetCountryNames()
+    {
+        $sortedCountries = array(
+            'AT' => 'Austria',
+            'DE' => 'Germany',
+        );
+
+        $this->reader->expects($this->once())
+            ->method('readEntry')
+            ->with(self::RES_DIR, 'en', array('Countries'))
+            ->will($this->returnValue($sortedCountries));
+
+        $this->assertSame($sortedCountries, $this->bundle->getCountryNames('en'));
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Util/RingBufferTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Util/RingBufferTest.php
new file mode 100644 (file)
index 0000000..d6f7d8a
--- /dev/null
@@ -0,0 +1,101 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\ResourceBundle\Util;
+
+use Symfony\Component\Intl\ResourceBundle\Util\RingBuffer;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class RingBufferTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var RingBuffer
+     */
+    private $buffer;
+
+    protected function setUp()
+    {
+        $this->buffer = new RingBuffer(2);
+    }
+
+    public function testWriteWithinBuffer()
+    {
+        $this->buffer[0] = 'foo';
+        $this->buffer['bar'] = 'baz';
+
+        $this->assertTrue(isset($this->buffer[0]));
+        $this->assertTrue(isset($this->buffer['bar']));
+        $this->assertSame('foo', $this->buffer[0]);
+        $this->assertSame('baz', $this->buffer['bar']);
+    }
+
+    public function testWritePastBuffer()
+    {
+        $this->buffer[0] = 'foo';
+        $this->buffer['bar'] = 'baz';
+        $this->buffer[2] = 'bam';
+
+        $this->assertTrue(isset($this->buffer['bar']));
+        $this->assertTrue(isset($this->buffer[2]));
+        $this->assertSame('baz', $this->buffer['bar']);
+        $this->assertSame('bam', $this->buffer[2]);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\OutOfBoundsException
+     */
+    public function testReadNonExistingFails()
+    {
+        $this->buffer['foo'];
+    }
+
+    public function testQueryNonExisting()
+    {
+        $this->assertFalse(isset($this->buffer['foo']));
+    }
+
+    public function testUnsetNonExistingSucceeds()
+    {
+        unset($this->buffer['foo']);
+
+        $this->assertFalse(isset($this->buffer['foo']));
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Intl\Exception\OutOfBoundsException
+     */
+    public function testReadOverwrittenFails()
+    {
+        $this->buffer[0] = 'foo';
+        $this->buffer['bar'] = 'baz';
+        $this->buffer[2] = 'bam';
+
+        $this->buffer[0];
+    }
+
+    public function testQueryOverwritten()
+    {
+        $this->assertFalse(isset($this->buffer[0]));
+    }
+
+    public function testUnsetOverwrittenSucceeds()
+    {
+        $this->buffer[0] = 'foo';
+        $this->buffer['bar'] = 'baz';
+        $this->buffer[2] = 'bam';
+
+        unset($this->buffer[0]);
+
+        $this->assertFalse(isset($this->buffer[0]));
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/en.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/en.php
new file mode 100644 (file)
index 0000000..1ded57a
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+    'Entry1' => array(
+        'Array' => array(
+            0 => 'foo',
+            1 => 'bar',
+        ),
+        'Integer' => 5,
+        'Boolean' => false,
+        'Float' => 1.23,
+    ),
+    'Entry2' => 'String',
+);
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/en.res b/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/en.res
new file mode 100644 (file)
index 0000000..7c1f71e
Binary files /dev/null and b/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/en.res differ
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/en.txt b/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/en.txt
new file mode 100644 (file)
index 0000000..0ee0d7f
--- /dev/null
@@ -0,0 +1,23 @@
+en{
+    Entry1{
+        Array{
+            "foo",
+            "bar",
+            {
+                Key{"value"}
+            },
+        }
+        Integer:int{5}
+        IntVector:intvector{
+            0,
+            1,
+            2,
+            3,
+        }
+        FalseBoolean{"false"}
+        TrueBoolean{"true"}
+        Null{""}
+        Float{"1.23"}
+    }
+    Entry2{"String"}
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/PhpBundleWriterTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/PhpBundleWriterTest.php
new file mode 100644 (file)
index 0000000..0330283
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\ResourceBundle\Writer;
+
+use Symfony\Component\Filesystem\Filesystem;
+use Symfony\Component\Intl\ResourceBundle\Writer\PhpBundleWriter;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class PhpBundleWriterTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var PhpBundleWriter
+     */
+    private $writer;
+
+    private $directory;
+
+    /**
+     * @var Filesystem
+     */
+    private $filesystem;
+
+    protected function setUp()
+    {
+        $this->writer = new PhpBundleWriter();
+        $this->directory = sys_get_temp_dir() . '/PhpBundleWriterTest/' . rand(1000, 9999);
+        $this->filesystem = new Filesystem();
+
+        $this->filesystem->mkdir($this->directory);
+    }
+
+    protected function tearDown()
+    {
+        $this->filesystem->remove($this->directory);
+    }
+
+    public function testWrite()
+    {
+        $this->writer->write($this->directory, 'en', array(
+            'Entry1' => array(
+                'Array' => array('foo', 'bar'),
+                'Integer' => 5,
+                'Boolean' => false,
+                'Float' => 1.23,
+            ),
+            'Entry2' => 'String',
+        ));
+
+        $this->assertFileEquals(__DIR__ . '/Fixtures/en.php', $this->directory . '/en.php');
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/TextBundleWriterTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/TextBundleWriterTest.php
new file mode 100644 (file)
index 0000000..cbe0c8d
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\ResourceBundle\Writer;
+
+use Symfony\Component\Filesystem\Filesystem;
+use Symfony\Component\Intl\ResourceBundle\Writer\TextBundleWriter;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ *
+ * @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
+ */
+class TextBundleWriterTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var TextBundleWriter
+     */
+    private $writer;
+
+    private $directory;
+
+    /**
+     * @var Filesystem
+     */
+    private $filesystem;
+
+    protected function setUp()
+    {
+        $this->writer = new TextBundleWriter();
+        $this->directory = sys_get_temp_dir() . '/TextBundleWriterTest/' . rand(1000, 9999);
+        $this->filesystem = new Filesystem();
+
+        $this->filesystem->mkdir($this->directory);
+    }
+
+    protected function tearDown()
+    {
+        $this->filesystem->remove($this->directory);
+    }
+
+    public function testWrite()
+    {
+        $this->writer->write($this->directory, 'en', array(
+            'Entry1' => array(
+                'Array' => array('foo', 'bar', array('Key' => 'value')),
+                'Integer' => 5,
+                'IntVector' => array(0, 1, 2, 3),
+                'FalseBoolean' => false,
+                'TrueBoolean' => true,
+                'Null' => null,
+                'Float' => 1.23,
+            ),
+            'Entry2' => 'String',
+        ));
+
+        $this->assertFileEquals(__DIR__ . '/Fixtures/en.txt', $this->directory . '/en.txt');
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/Util/IcuVersionTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/Util/IcuVersionTest.php
new file mode 100644 (file)
index 0000000..d1a7e8c
--- /dev/null
@@ -0,0 +1,111 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\Util;
+
+use Symfony\Component\Intl\Util\IcuVersion;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class IcuVersionTest extends \PHPUnit_Framework_TestCase
+{
+    public function normalizeProvider()
+    {
+        return array(
+            array(null, '1', '10'),
+            array(null, '1.2', '12'),
+            array(null, '1.2.3', '12.3'),
+            array(null, '1.2.3.4', '12.3.4'),
+            array(1, '1', '10'),
+            array(1, '1.2', '12'),
+            array(1, '1.2.3', '12'),
+            array(1, '1.2.3.4', '12'),
+            array(2, '1', '10'),
+            array(2, '1.2', '12'),
+            array(2, '1.2.3', '12.3'),
+            array(2, '1.2.3.4', '12.3'),
+            array(3, '1', '10'),
+            array(3, '1.2', '12'),
+            array(3, '1.2.3', '12.3'),
+            array(3, '1.2.3.4', '12.3.4'),
+        );
+    }
+
+    /**
+     * @dataProvider normalizeProvider
+     */
+    public function testNormalize($precision, $version, $result)
+    {
+        $this->assertSame($result, IcuVersion::normalize($version, $precision));
+    }
+
+    public function compareProvider()
+    {
+        return array(
+            array(null, '1', '==', '1', true),
+            array(null, '1.0', '==', '1.1', false),
+            array(null, '1.0.0', '==', '1.0.1', false),
+            array(null, '1.0.0.0', '==', '1.0.0.1', false),
+            array(null, '1.0.0.0.0', '==', '1.0.0.0.1', false),
+
+            array(null, '1', '==', '10', true),
+            array(null, '1.0', '==', '11', false),
+            array(null, '1.0.0', '==', '10.1', false),
+            array(null, '1.0.0.0', '==', '10.0.1', false),
+            array(null, '1.0.0.0.0', '==', '10.0.0.1', false),
+
+            array(1, '1', '==', '1', true),
+            array(1, '1.0', '==', '1.1', false),
+            array(1, '1.0.0', '==', '1.0.1', true),
+            array(1, '1.0.0.0', '==', '1.0.0.1', true),
+            array(1, '1.0.0.0.0', '==', '1.0.0.0.1', true),
+
+            array(1, '1', '==', '10', true),
+            array(1, '1.0', '==', '11', false),
+            array(1, '1.0.0', '==', '10.1', true),
+            array(1, '1.0.0.0', '==', '10.0.1', true),
+            array(1, '1.0.0.0.0', '==', '10.0.0.1', true),
+
+            array(2, '1', '==', '1', true),
+            array(2, '1.0', '==', '1.1', false),
+            array(2, '1.0.0', '==', '1.0.1', false),
+            array(2, '1.0.0.0', '==', '1.0.0.1', true),
+            array(2, '1.0.0.0.0', '==', '1.0.0.0.1', true),
+
+            array(2, '1', '==', '10', true),
+            array(2, '1.0', '==', '11', false),
+            array(2, '1.0.0', '==', '10.1', false),
+            array(2, '1.0.0.0', '==', '10.0.1', true),
+            array(2, '1.0.0.0.0', '==', '10.0.0.1', true),
+
+            array(3, '1', '==', '1', true),
+            array(3, '1.0', '==', '1.1', false),
+            array(3, '1.0.0', '==', '1.0.1', false),
+            array(3, '1.0.0.0', '==', '1.0.0.1', false),
+            array(3, '1.0.0.0.0', '==', '1.0.0.0.1', true),
+
+            array(3, '1', '==', '10', true),
+            array(3, '1.0', '==', '11', false),
+            array(3, '1.0.0', '==', '10.1', false),
+            array(3, '1.0.0.0', '==', '10.0.1', false),
+            array(3, '1.0.0.0.0', '==', '10.0.0.1', true),
+        );
+    }
+
+    /**
+     * @dataProvider compareProvider
+     */
+    public function testCompare($precision, $version1, $operator, $version2, $result)
+    {
+        $this->assertSame($result, IcuVersion::compare($version1, $version2, $operator, $precision));
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Tests/Util/VersionTest.php b/vendor/symfony/intl/Symfony/Component/Intl/Tests/Util/VersionTest.php
new file mode 100644 (file)
index 0000000..60cec6c
--- /dev/null
@@ -0,0 +1,87 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Tests\Util;
+
+use Symfony\Component\Intl\Util\Version;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class VersionTest extends \PHPUnit_Framework_TestCase
+{
+    public function normalizeProvider()
+    {
+        return array(
+            array(null, '1', '1'),
+            array(null, '1.2', '1.2'),
+            array(null, '1.2.3', '1.2.3'),
+            array(null, '1.2.3.4', '1.2.3.4'),
+            array(1, '1', '1'),
+            array(1, '1.2', '1'),
+            array(1, '1.2.3', '1'),
+            array(1, '1.2.3.4', '1'),
+            array(2, '1', '1'),
+            array(2, '1.2', '1.2'),
+            array(2, '1.2.3', '1.2'),
+            array(2, '1.2.3.4', '1.2'),
+            array(3, '1', '1'),
+            array(3, '1.2', '1.2'),
+            array(3, '1.2.3', '1.2.3'),
+            array(3, '1.2.3.4', '1.2.3'),
+            array(4, '1', '1'),
+            array(4, '1.2', '1.2'),
+            array(4, '1.2.3', '1.2.3'),
+            array(4, '1.2.3.4', '1.2.3.4'),
+        );
+    }
+
+    /**
+     * @dataProvider normalizeProvider
+     */
+    public function testNormalize($precision, $version, $result)
+    {
+        $this->assertSame($result, Version::normalize($version, $precision));
+    }
+
+    public function compareProvider()
+    {
+        return array(
+            array(null, '1', '==', '1', true),
+            array(null, '1.0', '==', '1.1', false),
+            array(null, '1.0.0', '==', '1.0.1', false),
+            array(null, '1.0.0.0', '==', '1.0.0.1', false),
+
+            array(1, '1', '==', '1', true),
+            array(1, '1.0', '==', '1.1', true),
+            array(1, '1.0.0', '==', '1.0.1', true),
+            array(1, '1.0.0.0', '==', '1.0.0.1', true),
+
+            array(2, '1', '==', '1', true),
+            array(2, '1.0', '==', '1.1', false),
+            array(2, '1.0.0', '==', '1.0.1', true),
+            array(2, '1.0.0.0', '==', '1.0.0.1', true),
+
+            array(3, '1', '==', '1', true),
+            array(3, '1.0', '==', '1.1', false),
+            array(3, '1.0.0', '==', '1.0.1', false),
+            array(3, '1.0.0.0', '==', '1.0.0.1', true),
+        );
+    }
+
+    /**
+     * @dataProvider compareProvider
+     */
+    public function testCompare($precision, $version1, $operator, $version2, $result)
+    {
+        $this->assertSame($result, Version::compare($version1, $version2, $operator, $precision));
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Util/IcuVersion.php b/vendor/symfony/intl/Symfony/Component/Intl/Util/IcuVersion.php
new file mode 100644 (file)
index 0000000..e305a07
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Util;
+
+/**
+ * Facilitates the comparison of ICU version strings.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class IcuVersion
+{
+    /**
+     * Compares two ICU versions with an operator.
+     *
+     * This method is identical to {@link version_compare()}, except that you
+     * can pass the number of regarded version components in the last argument
+     * $precision.
+     *
+     * Also, a single digit release version and a single digit major version
+     * are contracted to a two digit release version. If no major version
+     * is given, it is substituted by zero.
+     *
+     * Examples:
+     *
+     *     IcuVersion::compare('1.2.3', '1.2.4', '==')
+     *     // => false
+     *
+     *     IcuVersion::compare('1.2.3', '1.2.4', '==', 2)
+     *     // => true
+     *
+     *     IcuVersion::compare('1.2.3', '12.3', '==')
+     *     // => true
+     *
+     *     IcuVersion::compare('1', '10', '==')
+     *     // => true
+     *
+     * @param string       $version1  A version string.
+     * @param string       $version2  A version string to compare.
+     * @param string       $operator  The comparison operator.
+     * @param integer|null $precision The number of components to compare. Pass
+     *                                NULL to compare the versions unchanged.
+     *
+     * @return Boolean Whether the comparison succeeded.
+     *
+     * @see normalize()
+     */
+    public static function compare($version1, $version2, $operator, $precision = null)
+    {
+        $version1 = self::normalize($version1, $precision);
+        $version2 = self::normalize($version2, $precision);
+
+        return version_compare($version1, $version2, $operator);
+    }
+
+    /**
+     * Normalizes a version string to the number of components given in the
+     * parameter $precision.
+     *
+     * A single digit release version and a single digit major version are
+     * contracted to a two digit release version. If no major version is given,
+     * it is substituted by zero.
+     *
+     * Examples:
+     *
+     *     IcuVersion::normalize('1.2.3.4');
+     *     // => '12.3.4'
+     *
+     *     IcuVersion::normalize('1.2.3.4', 1);
+     *     // => '12'
+     *
+     *     IcuVersion::normalize('1.2.3.4', 2);
+     *     // => '12.3'
+     *
+     * @param string       $version   An ICU version string.
+     * @param integer|null $precision The number of components to include. Pass
+     *                                NULL to return the version unchanged.
+     *
+     * @return string|null The normalized ICU version or NULL if it couldn't be
+     *                     normalized.
+     */
+    public static function normalize($version, $precision)
+    {
+        $version = preg_replace('/^(\d)\.(\d)/', '$1$2', $version);
+
+        if (1 === strlen($version)) {
+            $version .= '0';
+        }
+
+        return Version::normalize($version, $precision);
+    }
+
+    /**
+     * Must not be instantiated.
+     */
+    private function __construct() {}
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Util/IntlTestHelper.php b/vendor/symfony/intl/Symfony/Component/Intl/Util/IntlTestHelper.php
new file mode 100644 (file)
index 0000000..cace36c
--- /dev/null
@@ -0,0 +1,128 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Util;
+
+use Symfony\Component\Intl\Intl;
+
+/**
+ * Helper class for preparing test cases that rely on the Intl component.
+ *
+ * Any test that tests functionality relying on either the intl classes or
+ * the resource bundle data should call either of the methods
+ * {@link requireIntl()} or {@link requireFullIntl()}. Calling
+ * {@link requireFullIntl()} is only necessary if you use functionality in the
+ * test that is not provided by the stub intl implementation.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class IntlTestHelper
+{
+    /**
+     * Should be called before tests that work fine with the stub implementation.
+     *
+     * @param \PhpUnit_Framework_TestCase $testCase
+     */
+    public static function requireIntl(\PhpUnit_Framework_TestCase $testCase)
+    {
+        // We only run tests if the version is *one specific version*.
+        // This condition is satisfied if
+        //
+        //   * the intl extension is loaded with version Intl::getIcuStubVersion()
+        //   * the intl extension is not loaded
+
+        if (IcuVersion::compare(Intl::getIcuVersion(), Intl::getIcuStubVersion(), '!=', 1)) {
+            $testCase->markTestSkipped('Please change ICU version to ' . Intl::getIcuStubVersion());
+        }
+
+        if (IcuVersion::compare(Intl::getIcuDataVersion(), Intl::getIcuStubVersion(), '!=', 1)) {
+            $testCase->markTestSkipped('Please change the Icu component to version 1.0.x or 1.' . IcuVersion::normalize(Intl::getIcuStubVersion(), 1) . '.x');
+        }
+
+        // Normalize the default locale in case this is not done explicitly
+        // in the test
+        \Locale::setDefault('en');
+
+        // Consequently, tests will
+        //
+        //   * run only for one ICU version (see Intl::getIcuStubVersion())
+        //     there is no need to add control structures to your tests that
+        //     change the test depending on the ICU version.
+        //
+        // Tests should only rely on functionality that is implemented in the
+        // stub classes.
+    }
+
+    /**
+     * Should be called before tests that require a feature-complete intl
+     * implementation.
+     *
+     * @param \PhpUnit_Framework_TestCase $testCase
+     */
+    public static function requireFullIntl(\PhpUnit_Framework_TestCase $testCase)
+    {
+        // We only run tests if the intl extension is loaded...
+        if (!Intl::isExtensionLoaded()) {
+            $testCase->markTestSkipped('The intl extension is not available.');
+        }
+
+        // ... and only if the version is *one specific version* ...
+        if (IcuVersion::compare(Intl::getIcuVersion(), Intl::getIcuStubVersion(), '!=', 1)) {
+            $testCase->markTestSkipped('Please change ICU version to ' . Intl::getIcuStubVersion());
+        }
+
+        // ... and only if the data in the Icu component matches that version.
+        if (IcuVersion::compare(Intl::getIcuDataVersion(), Intl::getIcuStubVersion(), '!=', 1)) {
+            $testCase->markTestSkipped('Please change the Icu component to version 1.0.x or 1.' . IcuVersion::normalize(Intl::getIcuStubVersion(), 1) . '.x');
+        }
+
+        // Normalize the default locale in case this is not done explicitly
+        // in the test
+        \Locale::setDefault('en');
+
+        // Consequently, tests will
+        //
+        //   * run only for one ICU version (see Intl::getIcuStubVersion())
+        //     there is no need to add control structures to your tests that
+        //     change the test depending on the ICU version.
+        //   * always use the C intl classes
+        //   * always use the binary resource bundles (any locale is allowed)
+    }
+
+    /**
+     * Skips the test unless the current system has a 32bit architecture.
+     *
+     * @param \PhpUnit_Framework_TestCase $testCase
+     */
+    public static function require32Bit(\PhpUnit_Framework_TestCase $testCase)
+    {
+        if (4 !== PHP_INT_SIZE) {
+            $testCase->markTestSkipped('PHP must be compiled in 32 bit mode to run this test');
+        }
+    }
+
+    /**
+     * Skips the test unless the current system has a 64bit architecture.
+     *
+     * @param \PhpUnit_Framework_TestCase $testCase
+     */
+    public static function require64Bit(\PhpUnit_Framework_TestCase $testCase)
+    {
+        if (8 !== PHP_INT_SIZE) {
+            $testCase->markTestSkipped('PHP must be compiled in 64 bit mode to run this test');
+        }
+    }
+
+    /**
+     * Must not be instantiated.
+     */
+    private function __construct() {}
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Util/SvnCommit.php b/vendor/symfony/intl/Symfony/Component/Intl/Util/SvnCommit.php
new file mode 100644 (file)
index 0000000..483d92b
--- /dev/null
@@ -0,0 +1,66 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Util;
+
+/**
+ * An SVN commit.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class SvnCommit
+{
+    /**
+     * @var \SimpleXMLElement
+     */
+    private $svnInfo;
+
+    /**
+     * Creates a commit from the given "svn info" data.
+     *
+     * @param \SimpleXMLElement $svnInfo The XML result from the "svn info"
+     *                                   command.
+     */
+    public function __construct(\SimpleXMLElement $svnInfo)
+    {
+        $this->svnInfo = $svnInfo;
+    }
+
+    /**
+     * Returns the revision of the commit.
+     *
+     * @return string The revision of the commit.
+     */
+    public function getRevision()
+    {
+        return (string) $this->svnInfo['revision'];
+    }
+
+    /**
+     * Returns the author of the commit.
+     *
+     * @return string The author name.
+     */
+    public function getAuthor()
+    {
+        return (string) $this->svnInfo->author;
+    }
+
+    /**
+     * Returns the date of the commit.
+     *
+     * @return string The commit date.
+     */
+    public function getDate()
+    {
+        return (string) $this->svnInfo->date;
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Util/SvnRepository.php b/vendor/symfony/intl/Symfony/Component/Intl/Util/SvnRepository.php
new file mode 100644 (file)
index 0000000..fb44e3c
--- /dev/null
@@ -0,0 +1,141 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Util;
+
+use Symfony\Component\Filesystem\Filesystem;
+use Symfony\Component\Intl\Exception\RuntimeException;
+
+/**
+ * A SVN repository containing ICU data.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class SvnRepository
+{
+    /**
+     * @var string The path to the repository.
+     */
+    private $path;
+
+    /**
+     * @var \SimpleXMLElement
+     */
+    private $svnInfo;
+
+    /**
+     * @var SvnCommit
+     */
+    private $lastCommit;
+
+    /**
+     * Downloads the ICU data for the given version.
+     *
+     * @param string $url       The URL to download from.
+     * @param string $targetDir The directory in which to store the repository.
+     *
+     * @return SvnRepository The directory where the data is stored.
+     *
+     * @throws RuntimeException If an error occurs during the download.
+     */
+    public static function download($url, $targetDir)
+    {
+        exec('which svn', $output, $result);
+
+        if ($result !== 0) {
+            throw new RuntimeException('The command "svn" is not installed.');
+        }
+
+        $filesystem = new Filesystem();
+
+        if (!$filesystem->exists($targetDir . '/.svn')) {
+            $filesystem->remove($targetDir);
+            $filesystem->mkdir($targetDir);
+
+            exec('svn checkout ' . $url . ' ' . $targetDir, $output, $result);
+
+            if ($result !== 0) {
+                throw new RuntimeException('The SVN checkout of ' . $url . 'failed.');
+            }
+        }
+
+        return new static(realpath($targetDir));
+    }
+
+    /**
+     * Reads the SVN repository at the given path.
+     *
+     * @param string $path The path to the repository.
+     */
+    public function __construct($path)
+    {
+        $this->path = $path;
+    }
+
+    /**
+     * Returns the path to the repository.
+     *
+     * @return string The path to the repository.
+     */
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    /**
+     * Returns the URL of the repository.
+     *
+     * @return string The URL of the repository.
+     */
+    public function getUrl()
+    {
+        return (string) $this->getSvnInfo()->entry->url;
+    }
+
+    /**
+     * Returns the last commit of the repository.
+     *
+     * @return SvnCommit The last commit.
+     */
+    public function getLastCommit()
+    {
+        if (null === $this->lastCommit) {
+            $this->lastCommit = new SvnCommit($this->getSvnInfo()->entry->commit);
+        }
+
+        return $this->lastCommit;
+    }
+
+    /**
+     * Returns information about the SVN repository.
+     *
+     * @return \SimpleXMLElement The XML result from the "svn info" command.
+     *
+     * @throws RuntimeException If the "svn info" command failed.
+     */
+    private function getSvnInfo()
+    {
+        if (null === $this->svnInfo) {
+            exec('svn info --xml '.$this->path, $output, $result);
+
+            $svnInfo = simplexml_load_string(implode("\n", $output));
+
+            if ($result !== 0) {
+                throw new RuntimeException('svn info failed');
+            }
+
+            $this->svnInfo = $svnInfo;
+        }
+
+        return $this->svnInfo;
+    }
+
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/Util/Version.php b/vendor/symfony/intl/Symfony/Component/Intl/Util/Version.php
new file mode 100644 (file)
index 0000000..5f6a433
--- /dev/null
@@ -0,0 +1,96 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Util;
+
+/**
+ * Facilitates the comparison of version strings.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class Version
+{
+    /**
+     * Compares two versions with an operator.
+     *
+     * This method is identical to {@link version_compare()}, except that you
+     * can pass the number of regarded version components in the last argument
+     * $precision.
+     *
+     * Examples:
+     *
+     *     Version::compare('1.2.3', '1.2.4', '==')
+     *     // => false
+     *
+     *     Version::compare('1.2.3', '1.2.4', '==', 2)
+     *     // => true
+     *
+     * @param string       $version1  A version string.
+     * @param string       $version2  A version string to compare.
+     * @param string       $operator  The comparison operator.
+     * @param integer|null $precision The number of components to compare. Pass
+     *                                NULL to compare the versions unchanged.
+     *
+     * @return Boolean Whether the comparison succeeded.
+     *
+     * @see normalize()
+     */
+    public static function compare($version1, $version2, $operator, $precision = null)
+    {
+        $version1 = self::normalize($version1, $precision);
+        $version2 = self::normalize($version2, $precision);
+
+        return version_compare($version1, $version2, $operator);
+    }
+
+    /**
+     * Normalizes a version string to the number of components given in the
+     * parameter $precision.
+     *
+     * Examples:
+     *
+     *     Version::normalize('1.2.3', 1);
+     *     // => '1'
+     *
+     *     Version::normalize('1.2.3', 2);
+     *     // => '1.2'
+     *
+     * @param string       $version   A version string.
+     * @param integer|null $precision The number of components to include. Pass
+     *                                NULL to return the version unchanged.
+     *
+     * @return string|null The normalized version or NULL if it couldn't be
+     *                     normalized.
+     */
+    public static function normalize($version, $precision)
+    {
+        if (null === $precision) {
+           return $version;
+        }
+
+        $pattern = '[^\.]+';
+
+        for ($i = 2; $i <= $precision; ++$i) {
+            $pattern = sprintf('[^\.]+(\.%s)?', $pattern);
+        }
+
+        if (!preg_match('/^' . $pattern . '/', $version, $matches)) {
+            return null;
+        }
+
+        return $matches[0];
+    }
+
+    /**
+     * Must not be instantiated.
+     */
+    private function __construct() {}
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/composer.json b/vendor/symfony/intl/Symfony/Component/Intl/composer.json
new file mode 100644 (file)
index 0000000..29eedfa
--- /dev/null
@@ -0,0 +1,48 @@
+{
+    "name": "symfony/intl",
+    "type": "library",
+    "description": "A PHP replacement layer for the C intl extension that includes additional data from the ICU library.",
+    "keywords": ["intl", "icu", "internationalization", "localization", "i18n", "l10n"],
+    "homepage": "http://symfony.com",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Bernhard Schussek",
+            "email": "bschussek@gmail.com"
+        },
+        {
+            "name": "Eriksen Costa",
+            "email": "eriksen.costa@infranology.com.br"
+        },
+        {
+            "name": "Igor Wiedler",
+            "email": "igor@wiedler.ch"
+        },
+        {
+            "name": "Symfony Community",
+            "homepage": "http://symfony.com/contributors"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.3",
+        "symfony/icu": "~1.0-RC"
+    },
+    "require-dev": {
+        "symfony/filesystem": ">=2.1"
+    },
+    "suggest": {
+        "ext-intl": "to use the component with locales other than \"en\""
+    },
+    "autoload": {
+        "psr-0": { "Symfony\\Component\\Intl\\": "" },
+        "classmap": [ "Symfony/Component/Intl/Resources/stubs" ],
+        "files": [ "Symfony/Component/Intl/Resources/stubs/functions.php" ]
+    },
+    "target-dir": "Symfony/Component/Intl",
+    "minimum-stability": "dev",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "2.3-dev"
+        }
+    }
+}
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/phpunit.xml.dist b/vendor/symfony/intl/Symfony/Component/Intl/phpunit.xml.dist
new file mode 100644 (file)
index 0000000..5e709f1
--- /dev/null
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         processIsolation="false"
+         stopOnFailure="false"
+         syntaxCheck="false"
+         bootstrap="vendor/autoload.php"
+>
+    <testsuites>
+        <testsuite name="Symfony Intl Component Test Suite">
+            <directory>./Tests/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./</directory>
+            <exclude>
+                <directory>./Tests</directory>
+                <directory>./vendor</directory>
+            </exclude>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/.gitignore b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/.gitignore
new file mode 100644 (file)
index 0000000..44de97a
--- /dev/null
@@ -0,0 +1,4 @@
+vendor/
+composer.lock
+phpunit.xml
+
diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/ExceptionInterface.php b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/ExceptionInterface.php
new file mode 100644 (file)
index 0000000..4224f4e
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver\Exception;
+
+/**
+ * Marker interface for the Options component.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+interface ExceptionInterface
+{
+}
diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/InvalidOptionsException.php b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/InvalidOptionsException.php
new file mode 100644 (file)
index 0000000..2e7ea1b
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver\Exception;
+
+/**
+ * Exception thrown when an invalid option is passed.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class InvalidOptionsException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/MissingOptionsException.php b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/MissingOptionsException.php
new file mode 100644 (file)
index 0000000..8544dfb
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver\Exception;
+
+/**
+ * Exception thrown when a required option is missing.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class MissingOptionsException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/OptionDefinitionException.php b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/OptionDefinitionException.php
new file mode 100644 (file)
index 0000000..11617fe
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver\Exception;
+
+/**
+ * Thrown when an option definition is invalid.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class OptionDefinitionException extends \RuntimeException implements ExceptionInterface
+{
+}
diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/LICENSE b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/LICENSE
new file mode 100644 (file)
index 0000000..88a57f8
--- /dev/null
@@ -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/options-resolver/Symfony/Component/OptionsResolver/Options.php b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Options.php
new file mode 100644 (file)
index 0000000..5b958af
--- /dev/null
@@ -0,0 +1,513 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver;
+
+use Symfony\Component\OptionsResolver\Exception\OptionDefinitionException;
+
+/**
+ * Container for resolving inter-dependent options.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class Options implements \ArrayAccess, \Iterator, \Countable
+{
+    /**
+     * A list of option values.
+     * @var array
+     */
+    private $options = array();
+
+    /**
+     * A list of normalizer closures.
+     * @var array
+     */
+    private $normalizers = array();
+
+    /**
+     * A list of closures for evaluating lazy options.
+     * @var array
+     */
+    private $lazy = array();
+
+    /**
+     * A list containing the currently locked options.
+     * @var array
+     */
+    private $lock = array();
+
+    /**
+     * Whether at least one option has already been read.
+     *
+     * Once read, the options cannot be changed anymore. This is
+     * necessary in order to avoid inconsistencies during the resolving
+     * process. If any option is changed after being read, all evaluated
+     * lazy options that depend on this option would become invalid.
+     *
+     * @var Boolean
+     */
+    private $reading = false;
+
+    /**
+     * Sets the value of a given option.
+     *
+     * You can set lazy options by passing a closure with the following
+     * signature:
+     *
+     * <code>
+     * function (Options $options)
+     * </code>
+     *
+     * This closure will be evaluated once the option is read using
+     * {@link get()}. The closure has access to the resolved values of
+     * other options through the passed {@link Options} instance.
+     *
+     * @param string $option The name of the option.
+     * @param mixed  $value  The value of the option.
+     *
+     * @throws OptionDefinitionException If options have already been read.
+     *                                   Once options are read, the container
+     *                                   becomes immutable.
+     */
+    public function set($option, $value)
+    {
+        // Setting is not possible once an option is read, because then lazy
+        // options could manipulate the state of the object, leading to
+        // inconsistent results.
+        if ($this->reading) {
+            throw new OptionDefinitionException('Options cannot be set anymore once options have been read.');
+        }
+
+        // Setting is equivalent to overloading while discarding the previous
+        // option value
+        unset($this->options[$option]);
+        unset($this->lazy[$option]);
+
+        $this->overload($option, $value);
+    }
+
+    /**
+     * Sets the normalizer for a given option.
+     *
+     * Normalizers should be closures with the following signature:
+     *
+     * <code>
+     * function (Options $options, $value)
+     * </code>
+     *
+     * This closure will be evaluated once the option is read using
+     * {@link get()}. The closure has access to the resolved values of
+     * other options through the passed {@link Options} instance.
+     *
+     * @param string   $option     The name of the option.
+     * @param \Closure $normalizer The normalizer.
+     *
+     * @throws OptionDefinitionException If options have already been read.
+     *                                   Once options are read, the container
+     *                                   becomes immutable.
+     */
+    public function setNormalizer($option, \Closure $normalizer)
+    {
+        if ($this->reading) {
+            throw new OptionDefinitionException('Normalizers cannot be added anymore once options have been read.');
+        }
+
+        $this->normalizers[$option] = $normalizer;
+    }
+
+    /**
+     * Replaces the contents of the container with the given options.
+     *
+     * This method is a shortcut for {@link clear()} with subsequent
+     * calls to {@link set()}.
+     *
+     * @param array $options The options to set.
+     *
+     * @throws OptionDefinitionException If options have already been read.
+     *                                   Once options are read, the container
+     *                                   becomes immutable.
+     */
+    public function replace(array $options)
+    {
+        if ($this->reading) {
+            throw new OptionDefinitionException('Options cannot be replaced anymore once options have been read.');
+        }
+
+        $this->options = array();
+        $this->lazy = array();
+        $this->normalizers = array();
+
+        foreach ($options as $option => $value) {
+            $this->overload($option, $value);
+        }
+    }
+
+    /**
+     * Overloads the value of a given option.
+     *
+     * Contrary to {@link set()}, this method keeps the previous default
+     * value of the option so that you can access it if you pass a closure.
+     * Passed closures should have the following signature:
+     *
+     * <code>
+     * function (Options $options, $value)
+     * </code>
+     *
+     * The second parameter passed to the closure is the current default
+     * value of the option.
+     *
+     * @param string $option The option name.
+     * @param mixed  $value  The option value.
+     *
+     * @throws OptionDefinitionException If options have already been read.
+     *                                   Once options are read, the container
+     *                                   becomes immutable.
+     */
+    public function overload($option, $value)
+    {
+        if ($this->reading) {
+            throw new OptionDefinitionException('Options cannot be overloaded anymore once options have been read.');
+        }
+
+        // If an option is a closure that should be evaluated lazily, store it
+        // in the "lazy" property.
+        if ($value instanceof \Closure) {
+            $reflClosure = new \ReflectionFunction($value);
+            $params = $reflClosure->getParameters();
+
+            if (isset($params[0]) && null !== ($class = $params[0]->getClass()) && __CLASS__ === $class->name) {
+                // Initialize the option if no previous value exists
+                if (!isset($this->options[$option])) {
+                    $this->options[$option] = null;
+                }
+
+                // Ignore previous lazy options if the closure has no second parameter
+                if (!isset($this->lazy[$option]) || !isset($params[1])) {
+                    $this->lazy[$option] = array();
+                }
+
+                // Store closure for later evaluation
+                $this->lazy[$option][] = $value;
+
+                return;
+            }
+        }
+
+        // Remove lazy options by default
+        unset($this->lazy[$option]);
+
+        $this->options[$option] = $value;
+    }
+
+    /**
+     * Returns the value of the given option.
+     *
+     * If the option was a lazy option, it is evaluated now.
+     *
+     * @param string $option The option name.
+     *
+     * @return mixed The option value.
+     *
+     * @throws \OutOfBoundsException     If the option does not exist.
+     * @throws OptionDefinitionException If a cyclic dependency is detected
+     *                                   between two lazy options.
+     */
+    public function get($option)
+    {
+        $this->reading = true;
+
+        if (!array_key_exists($option, $this->options)) {
+            throw new \OutOfBoundsException(sprintf('The option "%s" does not exist.', $option));
+        }
+
+        if (isset($this->lazy[$option])) {
+            $this->resolve($option);
+        }
+
+        if (isset($this->normalizers[$option])) {
+            $this->normalize($option);
+        }
+
+        return $this->options[$option];
+    }
+
+    /**
+     * Returns whether the given option exists.
+     *
+     * @param string $option The option name.
+     *
+     * @return Boolean Whether the option exists.
+     */
+    public function has($option)
+    {
+        return array_key_exists($option, $this->options);
+    }
+
+    /**
+     * Removes the option with the given name.
+     *
+     * @param string $option The option name.
+     *
+     * @throws OptionDefinitionException If options have already been read.
+     *                                   Once options are read, the container
+     *                                   becomes immutable.
+     */
+    public function remove($option)
+    {
+        if ($this->reading) {
+            throw new OptionDefinitionException('Options cannot be removed anymore once options have been read.');
+        }
+
+        unset($this->options[$option]);
+        unset($this->lazy[$option]);
+        unset($this->normalizers[$option]);
+    }
+
+    /**
+     * Removes all options.
+     *
+     * @throws OptionDefinitionException If options have already been read.
+     *                                   Once options are read, the container
+     *                                   becomes immutable.
+     */
+    public function clear()
+    {
+        if ($this->reading) {
+            throw new OptionDefinitionException('Options cannot be cleared anymore once options have been read.');
+        }
+
+        $this->options = array();
+        $this->lazy = array();
+        $this->normalizers = array();
+    }
+
+    /**
+     * Returns the values of all options.
+     *
+     * Lazy options are evaluated at this point.
+     *
+     * @return array The option values.
+     */
+    public function all()
+    {
+        $this->reading = true;
+
+        // Performance-wise this is slightly better than
+        // while (null !== $option = key($this->lazy))
+        foreach ($this->lazy as $option => $closures) {
+            // Double check, in case the option has already been resolved
+            // by cascade in the previous cycles
+            if (isset($this->lazy[$option])) {
+                $this->resolve($option);
+            }
+        }
+
+        foreach ($this->normalizers as $option => $normalizer) {
+            if (isset($this->normalizers[$option])) {
+                $this->normalize($option);
+            }
+        }
+
+        return $this->options;
+    }
+
+    /**
+     * Equivalent to {@link has()}.
+     *
+     * @param string $option The option name.
+     *
+     * @return Boolean Whether the option exists.
+     *
+     * @see \ArrayAccess::offsetExists()
+     */
+    public function offsetExists($option)
+    {
+        return $this->has($option);
+    }
+
+    /**
+     * Equivalent to {@link get()}.
+     *
+     * @param string $option The option name.
+     *
+     * @return mixed The option value.
+     *
+     * @throws \OutOfBoundsException     If the option does not exist.
+     * @throws OptionDefinitionException If a cyclic dependency is detected
+     *                                   between two lazy options.
+     *
+     * @see \ArrayAccess::offsetGet()
+     */
+    public function offsetGet($option)
+    {
+        return $this->get($option);
+    }
+
+    /**
+     * Equivalent to {@link set()}.
+     *
+     * @param string $option The name of the option.
+     * @param mixed  $value  The value of the option. May be a closure with a
+     *                       signature as defined in DefaultOptions::add().
+     *
+     * @throws OptionDefinitionException If options have already been read.
+     *                                   Once options are read, the container
+     *                                   becomes immutable.
+     *
+     * @see \ArrayAccess::offsetSet()
+     */
+    public function offsetSet($option, $value)
+    {
+        $this->set($option, $value);
+    }
+
+    /**
+     * Equivalent to {@link remove()}.
+     *
+     * @param string $option The option name.
+     *
+     * @throws OptionDefinitionException If options have already been read.
+     *                                   Once options are read, the container
+     *                                   becomes immutable.
+     *
+     * @see \ArrayAccess::offsetUnset()
+     */
+    public function offsetUnset($option)
+    {
+        $this->remove($option);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function current()
+    {
+        return $this->get($this->key());
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function next()
+    {
+        next($this->options);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function key()
+    {
+        return key($this->options);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function valid()
+    {
+        return null !== $this->key();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function rewind()
+    {
+        reset($this->options);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function count()
+    {
+        return count($this->options);
+    }
+
+    /**
+     * Evaluates the given lazy option.
+     *
+     * The evaluated value is written into the options array. The closure for
+     * evaluating the option is discarded afterwards.
+     *
+     * @param string $option The option to evaluate.
+     *
+     * @throws OptionDefinitionException If the option has a cyclic dependency
+     *                                   on another option.
+     */
+    private function resolve($option)
+    {
+        // The code duplication with normalize() exists for performance
+        // reasons, in order to save a method call.
+        // Remember that this method is potentially called a couple of thousand
+        // times and needs to be as efficient as possible.
+        if (isset($this->lock[$option])) {
+            $conflicts = array();
+
+            foreach ($this->lock as $option => $locked) {
+                if ($locked) {
+                    $conflicts[] = $option;
+                }
+            }
+
+            throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', implode('", "', $conflicts)));
+        }
+
+        $this->lock[$option] = true;
+        foreach ($this->lazy[$option] as $closure) {
+            $this->options[$option] = $closure($this, $this->options[$option]);
+        }
+        unset($this->lock[$option]);
+
+        // The option now isn't lazy anymore
+        unset($this->lazy[$option]);
+    }
+
+    /**
+     * Normalizes the given  option.
+     *
+     * The evaluated value is written into the options array.
+     *
+     * @param string $option The option to normalizer.
+     *
+     * @throws OptionDefinitionException If the option has a cyclic dependency
+     *                                   on another option.
+     */
+    private function normalize($option)
+    {
+        // The code duplication with resolve() exists for performance
+        // reasons, in order to save a method call.
+        // Remember that this method is potentially called a couple of thousand
+        // times and needs to be as efficient as possible.
+        if (isset($this->lock[$option])) {
+            $conflicts = array();
+
+            foreach ($this->lock as $option => $locked) {
+                if ($locked) {
+                    $conflicts[] = $option;
+                }
+            }
+
+            throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', implode('", "', $conflicts)));
+        }
+
+        /** @var \Closure $normalizer */
+        $normalizer = $this->normalizers[$option];
+
+        $this->lock[$option] = true;
+        $this->options[$option] = $normalizer($this, array_key_exists($option, $this->options) ? $this->options[$option] : null);
+        unset($this->lock[$option]);
+
+        // The option is now normalized
+        unset($this->normalizers[$option]);
+    }
+}
diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/OptionsResolver.php b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/OptionsResolver.php
new file mode 100644 (file)
index 0000000..d6554ba
--- /dev/null
@@ -0,0 +1,346 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver;
+
+use Symfony\Component\OptionsResolver\Exception\OptionDefinitionException;
+use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
+use Symfony\Component\OptionsResolver\Exception\MissingOptionsException;
+
+/**
+ * Helper for merging default and concrete option values.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ * @author Tobias Schultze <http://tobion.de>
+ */
+class OptionsResolver implements OptionsResolverInterface
+{
+    /**
+     * The default option values.
+     * @var Options
+     */
+    private $defaultOptions;
+
+    /**
+     * The options known by the resolver.
+     * @var array
+     */
+    private $knownOptions = array();
+
+    /**
+     * The options without defaults that are required to be passed to resolve().
+     * @var array
+     */
+    private $requiredOptions = array();
+
+    /**
+     * A list of accepted values for each option.
+     * @var array
+     */
+    private $allowedValues = array();
+
+    /**
+     * A list of accepted types for each option.
+     * @var array
+     */
+    private $allowedTypes = array();
+
+    /**
+     * Creates a new instance.
+     */
+    public function __construct()
+    {
+        $this->defaultOptions = new Options();
+    }
+
+    /**
+     * Clones the resolver.
+     */
+    public function __clone()
+    {
+        $this->defaultOptions = clone $this->defaultOptions;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setDefaults(array $defaultValues)
+    {
+        foreach ($defaultValues as $option => $value) {
+            $this->defaultOptions->overload($option, $value);
+            $this->knownOptions[$option] = true;
+            unset($this->requiredOptions[$option]);
+        }
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function replaceDefaults(array $defaultValues)
+    {
+        foreach ($defaultValues as $option => $value) {
+            $this->defaultOptions->set($option, $value);
+            $this->knownOptions[$option] = true;
+            unset($this->requiredOptions[$option]);
+        }
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setOptional(array $optionNames)
+    {
+        foreach ($optionNames as $key => $option) {
+            if (!is_int($key)) {
+                throw new OptionDefinitionException('You should not pass default values to setOptional()');
+            }
+
+            $this->knownOptions[$option] = true;
+        }
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setRequired(array $optionNames)
+    {
+        foreach ($optionNames as $key => $option) {
+            if (!is_int($key)) {
+                throw new OptionDefinitionException('You should not pass default values to setRequired()');
+            }
+
+            $this->knownOptions[$option] = true;
+            // set as required if no default has been set already
+            if (!isset($this->defaultOptions[$option])) {
+                $this->requiredOptions[$option] = true;
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setAllowedValues(array $allowedValues)
+    {
+        $this->validateOptionsExistence($allowedValues);
+
+        $this->allowedValues = array_replace($this->allowedValues, $allowedValues);
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addAllowedValues(array $allowedValues)
+    {
+        $this->validateOptionsExistence($allowedValues);
+
+        $this->allowedValues = array_merge_recursive($this->allowedValues, $allowedValues);
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setAllowedTypes(array $allowedTypes)
+    {
+        $this->validateOptionsExistence($allowedTypes);
+
+        $this->allowedTypes = array_replace($this->allowedTypes, $allowedTypes);
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addAllowedTypes(array $allowedTypes)
+    {
+        $this->validateOptionsExistence($allowedTypes);
+
+        $this->allowedTypes = array_merge_recursive($this->allowedTypes, $allowedTypes);
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setNormalizers(array $normalizers)
+    {
+        $this->validateOptionsExistence($normalizers);
+
+        foreach ($normalizers as $option => $normalizer) {
+            $this->defaultOptions->setNormalizer($option, $normalizer);
+        }
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isKnown($option)
+    {
+        return isset($this->knownOptions[$option]);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isRequired($option)
+    {
+        return isset($this->requiredOptions[$option]);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function resolve(array $options = array())
+    {
+        $this->validateOptionsExistence($options);
+        $this->validateOptionsCompleteness($options);
+
+        // Make sure this method can be called multiple times
+        $combinedOptions = clone $this->defaultOptions;
+
+        // Override options set by the user
+        foreach ($options as $option => $value) {
+            $combinedOptions->set($option, $value);
+        }
+
+        // Resolve options
+        $resolvedOptions = $combinedOptions->all();
+
+        $this->validateOptionValues($resolvedOptions);
+        $this->validateOptionTypes($resolvedOptions);
+
+        return $resolvedOptions;
+    }
+
+    /**
+     * Validates that the given option names exist and throws an exception
+     * otherwise.
+     *
+     * @param array $options An list of option names as keys.
+     *
+     * @throws InvalidOptionsException If any of the options has not been defined.
+     */
+    private function validateOptionsExistence(array $options)
+    {
+        $diff = array_diff_key($options, $this->knownOptions);
+
+        if (count($diff) > 0) {
+            ksort($this->knownOptions);
+            ksort($diff);
+
+            throw new InvalidOptionsException(sprintf(
+                (count($diff) > 1 ? 'The options "%s" do not exist.' : 'The option "%s" does not exist.').' Known options are: "%s"',
+                implode('", "', array_keys($diff)),
+                implode('", "', array_keys($this->knownOptions))
+            ));
+        }
+    }
+
+    /**
+     * Validates that all required options are given and throws an exception
+     * otherwise.
+     *
+     * @param array $options An list of option names as keys.
+     *
+     * @throws MissingOptionsException If a required option is missing.
+     */
+    private function validateOptionsCompleteness(array $options)
+    {
+        $diff = array_diff_key($this->requiredOptions, $options);
+
+        if (count($diff) > 0) {
+            ksort($diff);
+
+            throw new MissingOptionsException(sprintf(
+                count($diff) > 1 ? 'The required options "%s" are missing.' : 'The required option "%s" is  missing.',
+                implode('", "', array_keys($diff))
+            ));
+        }
+    }
+
+    /**
+     * Validates that the given option values match the allowed values and
+     * throws an exception otherwise.
+     *
+     * @param array $options A list of option values.
+     *
+     * @throws InvalidOptionsException If any of the values does not match the
+     *                                 allowed values of the option.
+     */
+    private function validateOptionValues(array $options)
+    {
+        foreach ($this->allowedValues as $option => $allowedValues) {
+            if (isset($options[$option]) && !in_array($options[$option], $allowedValues, true)) {
+                throw new InvalidOptionsException(sprintf('The option "%s" has the value "%s", but is expected to be one of "%s"', $option, $options[$option], implode('", "', $allowedValues)));
+            }
+        }
+    }
+
+    /**
+     * Validates that the given options match the allowed types and
+     * throws an exception otherwise.
+     *
+     * @param array $options A list of options.
+     *
+     * @throws InvalidOptionsException If any of the types does not match the
+     *                                 allowed types of the option.
+     */
+    private function validateOptionTypes(array $options)
+    {
+        foreach ($this->allowedTypes as $option => $allowedTypes) {
+            if (!array_key_exists($option, $options)) {
+                continue;
+            }
+
+            $value = $options[$option];
+            $allowedTypes = (array) $allowedTypes;
+
+            foreach ($allowedTypes as $type) {
+                $isFunction = 'is_'.$type;
+
+                if (function_exists($isFunction) && $isFunction($value)) {
+                    continue 2;
+                } elseif ($value instanceof $type) {
+                    continue 2;
+                }
+            }
+
+            $printableValue = is_object($value)
+                ? get_class($value)
+                : (is_array($value)
+                    ? 'Array'
+                    : (string) $value);
+
+            throw new InvalidOptionsException(sprintf(
+                'The option "%s" with value "%s" is expected to be of type "%s"',
+                $option,
+                $printableValue,
+                implode('", "', $allowedTypes)
+            ));
+        }
+    }
+}
diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/OptionsResolverInterface.php b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/OptionsResolverInterface.php
new file mode 100644 (file)
index 0000000..8474c4b
--- /dev/null
@@ -0,0 +1,210 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+interface OptionsResolverInterface
+{
+    /**
+     * Sets default option values.
+     *
+     * The options can either be values of any types or closures that
+     * evaluate the option value lazily. These closures must have one
+     * of the following signatures:
+     *
+     * <code>
+     * function (Options $options)
+     * function (Options $options, $value)
+     * </code>
+     *
+     * The second parameter passed to the closure is the previously
+     * set default value, in case you are overwriting an existing
+     * default value.
+     *
+     * The closures should return the lazily created option value.
+     *
+     * @param array $defaultValues A list of option names as keys and default
+     *                             values or closures as values.
+     *
+     * @return OptionsResolverInterface The resolver instance.
+     */
+    public function setDefaults(array $defaultValues);
+
+    /**
+     * Replaces default option values.
+     *
+     * Old defaults are erased, which means that closures passed here cannot
+     * access the previous default value. This may be useful to improve
+     * performance if the previous default value is calculated by an expensive
+     * closure.
+     *
+     * @param array $defaultValues A list of option names as keys and default
+     *                             values or closures as values.
+     *
+     * @return OptionsResolverInterface The resolver instance.
+     */
+    public function replaceDefaults(array $defaultValues);
+
+    /**
+     * Sets optional options.
+     *
+     * This method declares valid option names without setting default values for them.
+     * If these options are not passed to {@link resolve()} and no default has been set
+     * for them, they will be missing in the final options array. This can be helpful
+     * if you want to determine whether an option has been set or not because otherwise
+     * {@link resolve()} would trigger an exception for unknown options.
+     *
+     * @param array $optionNames A list of option names.
+     *
+     * @return OptionsResolverInterface The resolver instance.
+     *
+     * @throws Exception\OptionDefinitionException When trying to pass default values.
+     */
+    public function setOptional(array $optionNames);
+
+    /**
+     * Sets required options.
+     *
+     * If these options are not passed to {@link resolve()} and no default has been set for
+     * them, an exception will be thrown.
+     *
+     * @param array $optionNames A list of option names.
+     *
+     * @return OptionsResolverInterface The resolver instance.
+     *
+     * @throws Exception\OptionDefinitionException When trying to pass default values.
+     */
+    public function setRequired(array $optionNames);
+
+    /**
+     * Sets allowed values for a list of options.
+     *
+     * @param array $allowedValues A list of option names as keys and arrays
+     *                             with values acceptable for that option as
+     *                             values.
+     *
+     * @return OptionsResolverInterface The resolver instance.
+     *
+     * @throws Exception\InvalidOptionsException If an option has not been defined
+     *                                 (see {@link isKnown()}) for which
+     *                                 an allowed value is set.
+     */
+    public function setAllowedValues(array $allowedValues);
+
+    /**
+     * Adds allowed values for a list of options.
+     *
+     * The values are merged with the allowed values defined previously.
+     *
+     * @param array $allowedValues A list of option names as keys and arrays
+     *                             with values acceptable for that option as
+     *                             values.
+     *
+     * @return OptionsResolverInterface The resolver instance.
+     *
+     * @throws Exception\InvalidOptionsException If an option has not been defined
+     *                                 (see {@link isKnown()}) for which
+     *                                 an allowed value is set.
+     */
+    public function addAllowedValues(array $allowedValues);
+
+    /**
+     * Sets allowed types for a list of options.
+     *
+     * @param array $allowedTypes A list of option names as keys and type
+     *                            names passed as string or array as values.
+     *
+     * @return OptionsResolverInterface The resolver instance.
+     *
+     * @throws Exception\InvalidOptionsException If an option has not been defined for
+     *                                           which an allowed type is set.
+     */
+    public function setAllowedTypes(array $allowedTypes);
+
+    /**
+     * Adds allowed types for a list of options.
+     *
+     * The types are merged with the allowed types defined previously.
+     *
+     * @param array $allowedTypes A list of option names as keys and type
+     *                            names passed as string or array as values.
+     *
+     * @return OptionsResolverInterface The resolver instance.
+     *
+     * @throws Exception\InvalidOptionsException If an option has not been defined for
+     *                                           which an allowed type is set.
+     */
+    public function addAllowedTypes(array $allowedTypes);
+
+    /**
+     * Sets normalizers that are applied on resolved options.
+     *
+     * The normalizers should be closures with the following signature:
+     *
+     * <code>
+     * function (Options $options, $value)
+     * </code>
+     *
+     * The second parameter passed to the closure is the value of
+     * the option.
+     *
+     * The closure should return the normalized value.
+     *
+     * @param array $normalizers An array of closures.
+     *
+     * @return OptionsResolverInterface The resolver instance.
+     */
+    public function setNormalizers(array $normalizers);
+
+    /**
+     * Returns whether an option is known.
+     *
+     * An option is known if it has been passed to either {@link setDefaults()},
+     * {@link setRequired()} or {@link setOptional()} before.
+     *
+     * @param string $option The name of the option.
+     *
+     * @return Boolean Whether the option is known.
+     */
+    public function isKnown($option);
+
+    /**
+     * Returns whether an option is required.
+     *
+     * An option is required if it has been passed to {@link setRequired()},
+     * but not to {@link setDefaults()}. That is, the option has been declared
+     * as required and no default value has been set.
+     *
+     * @param string $option The name of the option.
+     *
+     * @return Boolean Whether the option is required.
+     */
+    public function isRequired($option);
+
+    /**
+     * Returns the combination of the default and the passed options.
+     *
+     * @param array $options The custom option values.
+     *
+     * @return array A list of options and their values.
+     *
+     * @throws Exception\InvalidOptionsException   If any of the passed options has not
+     *                                             been defined or does not contain an
+     *                                             allowed value.
+     * @throws Exception\MissingOptionsException   If a required option is missing.
+     * @throws Exception\OptionDefinitionException If a cyclic dependency is detected
+     *                                             between two lazy options.
+     */
+    public function resolve(array $options = array());
+}
diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/README.md b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/README.md
new file mode 100644 (file)
index 0000000..29cea10
--- /dev/null
@@ -0,0 +1,107 @@
+OptionsResolver Component
+=========================
+
+OptionsResolver helps at configuring objects with option arrays.
+
+It supports default values on different levels of your class hierarchy,
+option constraints (required vs. optional, allowed values) and lazy options
+whose default value depends on the value of another option.
+
+The following example demonstrates a Person class with two required options
+"firstName" and "lastName" and two optional options "age" and "gender", where
+the default value of "gender" is derived from the passed first name, if
+possible, and may only be one of "male" and "female".
+
+    use Symfony\Component\OptionsResolver\OptionsResolver;
+    use Symfony\Component\OptionsResolver\OptionsResolverInterface;
+    use Symfony\Component\OptionsResolver\Options;
+
+    class Person
+    {
+        protected $options;
+
+        public function __construct(array $options = array())
+        {
+            $resolver = new OptionsResolver();
+            $this->setDefaultOptions($resolver);
+
+            $this->options = $resolver->resolve($options);
+        }
+
+        protected function setDefaultOptions(OptionsResolverInterface $resolver)
+        {
+            $resolver->setRequired(array(
+                'firstName',
+                'lastName',
+            ));
+
+            $resolver->setDefaults(array(
+                'age' => null,
+                'gender' => function (Options $options) {
+                    if (self::isKnownMaleName($options['firstName'])) {
+                        return 'male';
+                    }
+
+                    return 'female';
+                },
+            ));
+
+            $resolver->setAllowedValues(array(
+                'gender' => array('male', 'female'),
+            ));
+        }
+    }
+
+We can now easily instantiate a Person object:
+
+    // 'gender' is implicitly set to 'female'
+    $person = new Person(array(
+        'firstName' => 'Jane',
+        'lastName' => 'Doe',
+    ));
+
+We can also override the default values of the optional options:
+
+    $person = new Person(array(
+        'firstName' => 'Abdullah',
+        'lastName' => 'Mogashi',
+        'gender' => 'male',
+        'age' => 30,
+    ));
+
+Options can be added or changed in subclasses by overriding the `setDefaultOptions`
+method:
+
+    use Symfony\Component\OptionsResolver\OptionsResolver;
+    use Symfony\Component\OptionsResolver\Options;
+
+    class Employee extends Person
+    {
+        protected function setDefaultOptions(OptionsResolverInterface $resolver)
+        {
+            parent::setDefaultOptions($resolver);
+
+            $resolver->setRequired(array(
+                'birthDate',
+            ));
+
+            $resolver->setDefaults(array(
+                // $previousValue contains the default value configured in the
+                // parent class
+                'age' => function (Options $options, $previousValue) {
+                    return self::calculateAge($options['birthDate']);
+                }
+            ));
+        }
+    }
+
+
+
+Resources
+---------
+
+You can run the unit tests with the following command:
+
+    $ cd path/to/Symfony/Component/OptionsResolver/
+    $ composer.phar install --dev
+    $ phpunit
diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php
new file mode 100644 (file)
index 0000000..d50cd3f
--- /dev/null
@@ -0,0 +1,681 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver\Tests;
+
+use Symfony\Component\OptionsResolver\OptionsResolver;
+use Symfony\Component\OptionsResolver\Options;
+
+class OptionsResolverTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var OptionsResolver
+     */
+    private $resolver;
+
+    protected function setUp()
+    {
+        $this->resolver = new OptionsResolver();
+    }
+
+    public function testResolve()
+    {
+        $this->resolver->setDefaults(array(
+            'one' => '1',
+            'two' => '2',
+        ));
+
+        $options = array(
+            'two' => '20',
+        );
+
+        $this->assertEquals(array(
+            'one' => '1',
+            'two' => '20',
+        ), $this->resolver->resolve($options));
+    }
+
+    public function testResolveLazy()
+    {
+        $this->resolver->setDefaults(array(
+            'one' => '1',
+            'two' => function (Options $options) {
+                return '20';
+            },
+        ));
+
+        $this->assertEquals(array(
+            'one' => '1',
+            'two' => '20',
+        ), $this->resolver->resolve(array()));
+    }
+
+    public function testResolveLazyDependencyOnOptional()
+    {
+        $this->resolver->setDefaults(array(
+            'one' => '1',
+            'two' => function (Options $options) {
+                return $options['one'].'2';
+            },
+        ));
+
+        $options = array(
+            'one' => '10',
+        );
+
+        $this->assertEquals(array(
+            'one' => '10',
+            'two' => '102',
+        ), $this->resolver->resolve($options));
+    }
+
+    public function testResolveLazyDependencyOnMissingOptionalWithoutDefault()
+    {
+        $test = $this;
+
+        $this->resolver->setOptional(array(
+            'one',
+        ));
+
+        $this->resolver->setDefaults(array(
+            'two' => function (Options $options) use ($test) {
+                /* @var \PHPUnit_Framework_TestCase $test */
+                $test->assertFalse(isset($options['one']));
+
+                return '2';
+            },
+        ));
+
+        $options = array(
+        );
+
+        $this->assertEquals(array(
+            'two' => '2',
+        ), $this->resolver->resolve($options));
+    }
+
+    public function testResolveLazyDependencyOnOptionalWithoutDefault()
+    {
+        $test = $this;
+
+        $this->resolver->setOptional(array(
+            'one',
+        ));
+
+        $this->resolver->setDefaults(array(
+            'two' => function (Options $options) use ($test) {
+                /* @var \PHPUnit_Framework_TestCase $test */
+                $test->assertTrue(isset($options['one']));
+
+                return $options['one'].'2';
+            },
+        ));
+
+        $options = array(
+            'one' => '10',
+        );
+
+        $this->assertEquals(array(
+            'one' => '10',
+            'two' => '102',
+        ), $this->resolver->resolve($options));
+    }
+
+    public function testResolveLazyDependencyOnRequired()
+    {
+        $this->resolver->setRequired(array(
+            'one',
+        ));
+        $this->resolver->setDefaults(array(
+            'two' => function (Options $options) {
+                return $options['one'].'2';
+            },
+        ));
+
+        $options = array(
+            'one' => '10',
+        );
+
+        $this->assertEquals(array(
+            'one' => '10',
+            'two' => '102',
+        ), $this->resolver->resolve($options));
+    }
+
+    public function testResolveLazyReplaceDefaults()
+    {
+        $test = $this;
+
+        $this->resolver->setDefaults(array(
+            'one' => function (Options $options) use ($test) {
+                /* @var \PHPUnit_Framework_TestCase $test */
+                $test->fail('Previous closure should not be executed');
+            },
+        ));
+
+        $this->resolver->replaceDefaults(array(
+            'one' => function (Options $options, $previousValue) {
+                return '1';
+            },
+        ));
+
+        $this->assertEquals(array(
+            'one' => '1',
+        ), $this->resolver->resolve(array()));
+    }
+
+    /**
+     * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+     */
+    public function testResolveFailsIfNonExistingOption()
+    {
+        $this->resolver->setDefaults(array(
+            'one' => '1',
+        ));
+
+        $this->resolver->setRequired(array(
+            'two',
+        ));
+
+        $this->resolver->setOptional(array(
+            'three',
+        ));
+
+        $this->resolver->resolve(array(
+            'foo' => 'bar',
+        ));
+    }
+
+    /**
+     * @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException
+     */
+    public function testResolveFailsIfMissingRequiredOption()
+    {
+        $this->resolver->setRequired(array(
+            'one',
+        ));
+
+        $this->resolver->setDefaults(array(
+            'two' => '2',
+        ));
+
+        $this->resolver->resolve(array(
+            'two' => '20',
+        ));
+    }
+
+    public function testResolveSucceedsIfOptionValueAllowed()
+    {
+        $this->resolver->setDefaults(array(
+            'one' => '1',
+        ));
+
+        $this->resolver->setAllowedValues(array(
+            'one' => array('1', 'one'),
+        ));
+
+        $options = array(
+            'one' => 'one',
+        );
+
+        $this->assertEquals(array(
+            'one' => 'one',
+        ), $this->resolver->resolve($options));
+    }
+
+    public function testResolveSucceedsIfOptionValueAllowed2()
+    {
+        $this->resolver->setDefaults(array(
+            'one' => '1',
+            'two' => '2',
+        ));
+
+        $this->resolver->setAllowedValues(array(
+            'one' => '1',
+            'two' => '2',
+        ));
+        $this->resolver->addAllowedValues(array(
+            'one' => 'one',
+            'two' => 'two',
+        ));
+
+        $options = array(
+            'one' => '1',
+            'two' => 'two',
+        );
+
+        $this->assertEquals(array(
+            'one' => '1',
+            'two' => 'two',
+        ), $this->resolver->resolve($options));
+    }
+
+    public function testResolveSucceedsIfOptionalWithAllowedValuesNotSet()
+    {
+        $this->resolver->setRequired(array(
+            'one',
+        ));
+
+        $this->resolver->setOptional(array(
+            'two',
+        ));
+
+        $this->resolver->setAllowedValues(array(
+            'one' => array('1', 'one'),
+            'two' => array('2', 'two'),
+        ));
+
+        $options = array(
+            'one' => '1',
+        );
+
+        $this->assertEquals(array(
+            'one' => '1',
+        ), $this->resolver->resolve($options));
+    }
+
+    /**
+     * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+     */
+    public function testResolveFailsIfOptionValueNotAllowed()
+    {
+        $this->resolver->setDefaults(array(
+            'one' => '1',
+        ));
+
+        $this->resolver->setAllowedValues(array(
+            'one' => array('1', 'one'),
+        ));
+
+        $this->resolver->resolve(array(
+            'one' => '2',
+        ));
+    }
+
+    public function testResolveSucceedsIfOptionTypeAllowed()
+    {
+        $this->resolver->setDefaults(array(
+            'one' => '1',
+        ));
+
+        $this->resolver->setAllowedTypes(array(
+            'one' => 'string',
+        ));
+
+        $options = array(
+            'one' => 'one',
+        );
+
+        $this->assertEquals(array(
+            'one' => 'one',
+        ), $this->resolver->resolve($options));
+    }
+
+    public function testResolveSucceedsIfOptionTypeAllowedPassArray()
+    {
+        $this->resolver->setDefaults(array(
+            'one' => '1',
+        ));
+
+        $this->resolver->setAllowedTypes(array(
+            'one' => array('string', 'bool'),
+        ));
+
+        $options = array(
+            'one' => true,
+        );
+
+        $this->assertEquals(array(
+            'one' => true,
+        ), $this->resolver->resolve($options));
+    }
+
+    public function testResolveSucceedsIfOptionTypeAllowedPassObject()
+    {
+        $this->resolver->setDefaults(array(
+            'one' => '1',
+        ));
+
+        $this->resolver->setAllowedTypes(array(
+            'one' => 'object',
+        ));
+
+        $object = new \stdClass();
+        $options = array(
+            'one' => $object,
+        );
+
+        $this->assertEquals(array(
+            'one' => $object,
+        ), $this->resolver->resolve($options));
+    }
+
+    public function testResolveSucceedsIfOptionTypeAllowedPassClass()
+    {
+        $this->resolver->setDefaults(array(
+            'one' => '1',
+        ));
+
+        $this->resolver->setAllowedTypes(array(
+            'one' => '\stdClass',
+        ));
+
+        $object = new \stdClass();
+        $options = array(
+            'one' => $object,
+        );
+
+        $this->assertEquals(array(
+            'one' => $object,
+        ), $this->resolver->resolve($options));
+    }
+
+    public function testResolveSucceedsIfOptionTypeAllowedAddTypes()
+    {
+        $this->resolver->setDefaults(array(
+            'one' => '1',
+            'two' => '2',
+        ));
+
+        $this->resolver->setAllowedTypes(array(
+            'one' => 'string',
+            'two' => 'bool',
+        ));
+        $this->resolver->addAllowedTypes(array(
+            'one' => 'float',
+            'two' => 'integer',
+        ));
+
+        $options = array(
+            'one' => 1.23,
+            'two' => false,
+        );
+
+        $this->assertEquals(array(
+            'one' => 1.23,
+            'two' => false,
+        ), $this->resolver->resolve($options));
+    }
+
+    public function testResolveSucceedsIfOptionalWithTypeAndWithoutValue()
+    {
+        $this->resolver->setOptional(array(
+            'one',
+            'two',
+        ));
+
+        $this->resolver->setAllowedTypes(array(
+            'one' => 'string',
+            'two' => 'int',
+        ));
+
+        $options = array(
+            'two' => 1,
+        );
+
+        $this->assertEquals(array(
+            'two' => 1,
+        ), $this->resolver->resolve($options));
+    }
+
+    /**
+     * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+     */
+    public function testResolveFailsIfOptionTypeNotAllowed()
+    {
+        $this->resolver->setDefaults(array(
+            'one' => '1',
+        ));
+
+        $this->resolver->setAllowedTypes(array(
+            'one' => array('string', 'bool'),
+        ));
+
+        $this->resolver->resolve(array(
+            'one' => 1.23,
+        ));
+    }
+
+    /**
+     * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+     */
+    public function testResolveFailsIfOptionTypeNotAllowedMultipleOptions()
+    {
+        $this->resolver->setDefaults(array(
+            'one' => '1',
+            'two' => '2',
+        ));
+
+        $this->resolver->setAllowedTypes(array(
+            'one' => 'string',
+            'two' => 'bool',
+        ));
+
+        $this->resolver->resolve(array(
+            'one' => 'foo',
+            'two' => 1.23,
+        ));
+    }
+
+    /**
+     * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+     */
+    public function testResolveFailsIfOptionTypeNotAllowedAddTypes()
+    {
+        $this->resolver->setDefaults(array(
+            'one' => '1',
+        ));
+
+        $this->resolver->setAllowedTypes(array(
+            'one' => 'string',
+        ));
+        $this->resolver->addAllowedTypes(array(
+            'one' => 'bool',
+        ));
+
+        $this->resolver->resolve(array(
+            'one' => 1.23,
+        ));
+    }
+
+    /**
+     * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
+     */
+    public function testSetRequiredFailsIfDefaultIsPassed()
+    {
+        $this->resolver->setRequired(array(
+            'one' => '1',
+        ));
+    }
+
+    /**
+     * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
+     */
+    public function testSetOptionalFailsIfDefaultIsPassed()
+    {
+        $this->resolver->setOptional(array(
+            'one' => '1',
+        ));
+    }
+
+    public function testFluidInterface()
+    {
+        $this->resolver->setDefaults(array('one' => '1'))
+            ->replaceDefaults(array('one' => '2'))
+            ->setAllowedValues(array('one' => array('1', '2')))
+            ->addAllowedValues(array('one' => array('3')))
+            ->setRequired(array('two'))
+            ->setOptional(array('three'));
+
+        $options = array(
+            'two' => '2',
+        );
+
+        $this->assertEquals(array(
+            'one' => '2',
+            'two' => '2',
+        ), $this->resolver->resolve($options));
+    }
+
+    public function testKnownIfDefaultWasSet()
+    {
+        $this->assertFalse($this->resolver->isKnown('foo'));
+
+        $this->resolver->setDefaults(array(
+            'foo' => 'bar',
+        ));
+
+        $this->assertTrue($this->resolver->isKnown('foo'));
+    }
+
+    public function testKnownIfRequired()
+    {
+        $this->assertFalse($this->resolver->isKnown('foo'));
+
+        $this->resolver->setRequired(array(
+            'foo',
+        ));
+
+        $this->assertTrue($this->resolver->isKnown('foo'));
+    }
+
+    public function testKnownIfOptional()
+    {
+        $this->assertFalse($this->resolver->isKnown('foo'));
+
+        $this->resolver->setOptional(array(
+            'foo',
+        ));
+
+        $this->assertTrue($this->resolver->isKnown('foo'));
+    }
+
+    public function testRequiredIfRequired()
+    {
+        $this->assertFalse($this->resolver->isRequired('foo'));
+
+        $this->resolver->setRequired(array(
+            'foo',
+        ));
+
+        $this->assertTrue($this->resolver->isRequired('foo'));
+    }
+
+    public function testNotRequiredIfRequiredAndDefaultValue()
+    {
+        $this->assertFalse($this->resolver->isRequired('foo'));
+
+        $this->resolver->setRequired(array(
+            'foo',
+        ));
+        $this->resolver->setDefaults(array(
+            'foo' => 'bar',
+        ));
+
+        $this->assertFalse($this->resolver->isRequired('foo'));
+    }
+
+    public function testNormalizersTransformFinalOptions()
+    {
+        $this->resolver->setDefaults(array(
+            'foo' => 'bar',
+            'bam' => 'baz',
+        ));
+        $this->resolver->setNormalizers(array(
+            'foo' => function (Options $options, $value) {
+                return $options['bam'].'['.$value.']';
+            },
+        ));
+
+        $expected = array(
+            'foo' => 'baz[bar]',
+            'bam' => 'baz',
+        );
+
+        $this->assertEquals($expected, $this->resolver->resolve(array()));
+
+        $expected = array(
+            'foo' => 'boo[custom]',
+            'bam' => 'boo',
+        );
+
+        $this->assertEquals($expected, $this->resolver->resolve(array(
+            'foo' => 'custom',
+            'bam' => 'boo',
+        )));
+    }
+
+    public function testResolveWithoutOptionSucceedsIfRequiredAndDefaultValue()
+    {
+        $this->resolver->setRequired(array(
+            'foo',
+        ));
+        $this->resolver->setDefaults(array(
+            'foo' => 'bar',
+        ));
+
+        $this->assertEquals(array(
+            'foo' => 'bar'
+        ), $this->resolver->resolve(array()));
+    }
+
+    public function testResolveWithoutOptionSucceedsIfDefaultValueAndRequired()
+    {
+        $this->resolver->setDefaults(array(
+            'foo' => 'bar',
+        ));
+        $this->resolver->setRequired(array(
+            'foo',
+        ));
+
+        $this->assertEquals(array(
+            'foo' => 'bar'
+        ), $this->resolver->resolve(array()));
+    }
+
+    public function testResolveSucceedsIfOptionRequiredAndValueAllowed()
+    {
+        $this->resolver->setRequired(array(
+            'one', 'two',
+        ));
+        $this->resolver->setAllowedValues(array(
+            'two' => array('2'),
+        ));
+
+        $options = array(
+            'one' => '1',
+            'two' => '2'
+        );
+
+        $this->assertEquals($options, $this->resolver->resolve($options));
+    }
+
+    public function testClone()
+    {
+        $this->resolver->setDefaults(array('one' => '1'));
+
+        $clone = clone $this->resolver;
+
+        // Changes after cloning don't affect each other
+        $this->resolver->setDefaults(array('two' => '2'));
+        $clone->setDefaults(array('three' => '3'));
+
+        $this->assertEquals(array(
+            'one' => '1',
+            'two' => '2',
+        ), $this->resolver->resolve());
+
+        $this->assertEquals(array(
+            'one' => '1',
+            'three' => '3',
+        ), $clone->resolve());
+    }
+}
diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Tests/OptionsTest.php b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Tests/OptionsTest.php
new file mode 100644 (file)
index 0000000..e24a764
--- /dev/null
@@ -0,0 +1,529 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver\Tests;
+
+use Symfony\Component\OptionsResolver\Options;
+
+class OptionsTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var Options
+     */
+    private $options;
+
+    protected function setUp()
+    {
+        $this->options = new Options();
+    }
+
+    public function testArrayAccess()
+    {
+        $this->assertFalse(isset($this->options['foo']));
+        $this->assertFalse(isset($this->options['bar']));
+
+        $this->options['foo'] = 0;
+        $this->options['bar'] = 1;
+
+        $this->assertTrue(isset($this->options['foo']));
+        $this->assertTrue(isset($this->options['bar']));
+
+        unset($this->options['bar']);
+
+        $this->assertTrue(isset($this->options['foo']));
+        $this->assertFalse(isset($this->options['bar']));
+        $this->assertEquals(0, $this->options['foo']);
+    }
+
+    public function testCountable()
+    {
+        $this->options->set('foo', 0);
+        $this->options->set('bar', 1);
+
+        $this->assertCount(2, $this->options);
+    }
+
+    /**
+     * @expectedException \OutOfBoundsException
+     */
+    public function testGetNonExisting()
+    {
+        $this->options->get('foo');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
+     */
+    public function testSetNotSupportedAfterGet()
+    {
+        $this->options->set('foo', 'bar');
+        $this->options->get('foo');
+        $this->options->set('foo', 'baz');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
+     */
+    public function testRemoveNotSupportedAfterGet()
+    {
+        $this->options->set('foo', 'bar');
+        $this->options->get('foo');
+        $this->options->remove('foo');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
+     */
+    public function testSetNormalizerNotSupportedAfterGet()
+    {
+        $this->options->set('foo', 'bar');
+        $this->options->get('foo');
+        $this->options->setNormalizer('foo', function () {});
+    }
+
+    public function testSetLazyOption()
+    {
+        $test = $this;
+
+        $this->options->set('foo', function (Options $options) use ($test) {
+           return 'dynamic';
+        });
+
+        $this->assertEquals('dynamic', $this->options->get('foo'));
+    }
+
+    public function testSetDiscardsPreviousValue()
+    {
+        $test = $this;
+
+        // defined by superclass
+        $this->options->set('foo', 'bar');
+
+        // defined by subclass
+        $this->options->set('foo', function (Options $options, $previousValue) use ($test) {
+            /* @var \PHPUnit_Framework_TestCase $test */
+            $test->assertNull($previousValue);
+
+            return 'dynamic';
+        });
+
+        $this->assertEquals('dynamic', $this->options->get('foo'));
+    }
+
+    public function testOverloadKeepsPreviousValue()
+    {
+        $test = $this;
+
+        // defined by superclass
+        $this->options->set('foo', 'bar');
+
+        // defined by subclass
+        $this->options->overload('foo', function (Options $options, $previousValue) use ($test) {
+            /* @var \PHPUnit_Framework_TestCase $test */
+            $test->assertEquals('bar', $previousValue);
+
+            return 'dynamic';
+        });
+
+        $this->assertEquals('dynamic', $this->options->get('foo'));
+    }
+
+    public function testPreviousValueIsEvaluatedIfLazy()
+    {
+        $test = $this;
+
+        // defined by superclass
+        $this->options->set('foo', function (Options $options) {
+            return 'bar';
+        });
+
+        // defined by subclass
+        $this->options->overload('foo', function (Options $options, $previousValue) use ($test) {
+            /* @var \PHPUnit_Framework_TestCase $test */
+            $test->assertEquals('bar', $previousValue);
+
+            return 'dynamic';
+        });
+
+        $this->assertEquals('dynamic', $this->options->get('foo'));
+    }
+
+    public function testPreviousValueIsNotEvaluatedIfNoSecondArgument()
+    {
+        $test = $this;
+
+        // defined by superclass
+        $this->options->set('foo', function (Options $options) use ($test) {
+            $test->fail('Should not be called');
+        });
+
+        // defined by subclass, no $previousValue argument defined!
+        $this->options->overload('foo', function (Options $options) use ($test) {
+            return 'dynamic';
+        });
+
+        $this->assertEquals('dynamic', $this->options->get('foo'));
+    }
+
+    public function testLazyOptionCanAccessOtherOptions()
+    {
+        $test = $this;
+
+        $this->options->set('foo', 'bar');
+
+        $this->options->set('bam', function (Options $options) use ($test) {
+            /* @var \PHPUnit_Framework_TestCase $test */
+            $test->assertEquals('bar', $options->get('foo'));
+
+            return 'dynamic';
+        });
+
+        $this->assertEquals('bar', $this->options->get('foo'));
+        $this->assertEquals('dynamic', $this->options->get('bam'));
+    }
+
+    public function testLazyOptionCanAccessOtherLazyOptions()
+    {
+        $test = $this;
+
+        $this->options->set('foo', function (Options $options) {
+            return 'bar';
+        });
+
+        $this->options->set('bam', function (Options $options) use ($test) {
+            /* @var \PHPUnit_Framework_TestCase $test */
+            $test->assertEquals('bar', $options->get('foo'));
+
+            return 'dynamic';
+        });
+
+        $this->assertEquals('bar', $this->options->get('foo'));
+        $this->assertEquals('dynamic', $this->options->get('bam'));
+    }
+
+    public function testNormalizer()
+    {
+        $this->options->set('foo', 'bar');
+
+        $this->options->setNormalizer('foo', function () {
+            return 'normalized';
+        });
+
+        $this->assertEquals('normalized', $this->options->get('foo'));
+    }
+
+    public function testNormalizerReceivesUnnormalizedValue()
+    {
+        $this->options->set('foo', 'bar');
+
+        $this->options->setNormalizer('foo', function (Options $options, $value) {
+            return 'normalized['.$value.']';
+        });
+
+        $this->assertEquals('normalized[bar]', $this->options->get('foo'));
+    }
+
+    public function testNormalizerCanAccessOtherOptions()
+    {
+        $test = $this;
+
+        $this->options->set('foo', 'bar');
+        $this->options->set('bam', 'baz');
+
+        $this->options->setNormalizer('bam', function (Options $options) use ($test) {
+            /* @var \PHPUnit_Framework_TestCase $test */
+            $test->assertEquals('bar', $options->get('foo'));
+
+            return 'normalized';
+        });
+
+        $this->assertEquals('bar', $this->options->get('foo'));
+        $this->assertEquals('normalized', $this->options->get('bam'));
+    }
+
+    public function testNormalizerCanAccessOtherLazyOptions()
+    {
+        $test = $this;
+
+        $this->options->set('foo', function (Options $options) {
+            return 'bar';
+        });
+        $this->options->set('bam', 'baz');
+
+        $this->options->setNormalizer('bam', function (Options $options) use ($test) {
+            /* @var \PHPUnit_Framework_TestCase $test */
+            $test->assertEquals('bar', $options->get('foo'));
+
+            return 'normalized';
+        });
+
+        $this->assertEquals('bar', $this->options->get('foo'));
+        $this->assertEquals('normalized', $this->options->get('bam'));
+    }
+
+    /**
+     * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
+     */
+    public function testFailForCyclicDependencies()
+    {
+        $this->options->set('foo', function (Options $options) {
+            $options->get('bam');
+        });
+
+        $this->options->set('bam', function (Options $options) {
+            $options->get('foo');
+        });
+
+        $this->options->get('foo');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
+     */
+    public function testFailForCyclicDependenciesBetweenNormalizers()
+    {
+        $this->options->set('foo', 'bar');
+        $this->options->set('bam', 'baz');
+
+        $this->options->setNormalizer('foo', function (Options $options) {
+            $options->get('bam');
+        });
+
+        $this->options->setNormalizer('bam', function (Options $options) {
+            $options->get('foo');
+        });
+
+        $this->options->get('foo');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
+     */
+    public function testFailForCyclicDependenciesBetweenNormalizerAndLazyOption()
+    {
+        $this->options->set('foo', function (Options $options) {
+            $options->get('bam');
+        });
+        $this->options->set('bam', 'baz');
+
+        $this->options->setNormalizer('bam', function (Options $options) {
+            $options->get('foo');
+        });
+
+        $this->options->get('foo');
+    }
+
+    public function testAllInvokesEachLazyOptionOnlyOnce()
+    {
+        $test = $this;
+        $i = 1;
+
+        $this->options->set('foo', function (Options $options) use ($test, &$i) {
+            $test->assertSame(1, $i);
+            ++$i;
+
+            // Implicitly invoke lazy option for "bam"
+            $options->get('bam');
+        });
+        $this->options->set('bam', function (Options $options) use ($test, &$i) {
+            $test->assertSame(2, $i);
+            ++$i;
+        });
+
+        $this->options->all();
+    }
+
+    public function testAllInvokesEachNormalizerOnlyOnce()
+    {
+        $test = $this;
+        $i = 1;
+
+        $this->options->set('foo', 'bar');
+        $this->options->set('bam', 'baz');
+
+        $this->options->setNormalizer('foo', function (Options $options) use ($test, &$i) {
+            $test->assertSame(1, $i);
+            ++$i;
+
+            // Implicitly invoke normalizer for "bam"
+            $options->get('bam');
+        });
+        $this->options->setNormalizer('bam', function (Options $options) use ($test, &$i) {
+            $test->assertSame(2, $i);
+            ++$i;
+        });
+
+        $this->options->all();
+    }
+
+    public function testReplaceClearsAndSets()
+    {
+        $this->options->set('one', '1');
+
+        $this->options->replace(array(
+            'two' => '2',
+            'three' => function (Options $options) {
+                return '2' === $options['two'] ? '3' : 'foo';
+            }
+        ));
+
+        $this->assertEquals(array(
+            'two' => '2',
+            'three' => '3',
+        ), $this->options->all());
+    }
+
+    public function testClearRemovesAllOptions()
+    {
+        $this->options->set('one', 1);
+        $this->options->set('two', 2);
+
+        $this->options->clear();
+
+        $this->assertEmpty($this->options->all());
+
+    }
+
+    /**
+     * @covers Symfony\Component\OptionsResolver\Options::replace
+     * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
+     */
+    public function testCannotReplaceAfterOptionWasRead()
+    {
+        $this->options->set('one', 1);
+        $this->options->all();
+
+        $this->options->replace(array(
+            'two' => '2',
+        ));
+    }
+
+    /**
+     * @covers Symfony\Component\OptionsResolver\Options::overload
+     * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
+     */
+    public function testCannotOverloadAfterOptionWasRead()
+    {
+        $this->options->set('one', 1);
+        $this->options->all();
+
+        $this->options->overload('one', 2);
+    }
+
+    /**
+     * @covers Symfony\Component\OptionsResolver\Options::clear
+     * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
+     */
+    public function testCannotClearAfterOptionWasRead()
+    {
+        $this->options->set('one', 1);
+        $this->options->all();
+
+        $this->options->clear();
+    }
+
+    public function testOverloadCannotBeEvaluatedLazilyWithoutExpectedClosureParams()
+    {
+        $this->options->set('foo', 'bar');
+
+        $this->options->overload('foo', function () {
+            return 'test';
+        });
+
+        $this->assertNotEquals('test', $this->options->get('foo'));
+        $this->assertTrue(is_callable($this->options->get('foo')));
+    }
+
+    public function testOverloadCannotBeEvaluatedLazilyWithoutFirstParamTypeHint()
+    {
+        $this->options->set('foo', 'bar');
+
+        $this->options->overload('foo', function ($object) {
+            return 'test';
+        });
+
+        $this->assertNotEquals('test', $this->options->get('foo'));
+        $this->assertTrue(is_callable($this->options->get('foo')));
+    }
+
+    public function testOptionsIteration()
+    {
+        $this->options->set('foo', 'bar');
+        $this->options->set('foo1', 'bar1');
+        $expectedResult = array('foo' => 'bar', 'foo1' => 'bar1');
+
+        $this->assertEquals($expectedResult, iterator_to_array($this->options, true));
+    }
+
+    public function testHasWithNullValue()
+    {
+        $this->options->set('foo', null);
+
+        $this->assertTrue($this->options->has('foo'));
+    }
+
+    public function testRemoveOptionAndNormalizer()
+    {
+        $this->options->set('foo1', 'bar');
+        $this->options->setNormalizer('foo1', function (Options $options) {
+            return '';
+        });
+        $this->options->set('foo2', 'bar');
+        $this->options->setNormalizer('foo2', function (Options $options) {
+            return '';
+        });
+
+        $this->options->remove('foo2');
+        $this->assertEquals(array('foo1' => ''), $this->options->all());
+    }
+
+    public function testReplaceOptionAndNormalizer()
+    {
+        $this->options->set('foo1', 'bar');
+        $this->options->setNormalizer('foo1', function (Options $options) {
+            return '';
+        });
+        $this->options->set('foo2', 'bar');
+        $this->options->setNormalizer('foo2', function (Options $options) {
+            return '';
+        });
+
+        $this->options->replace(array('foo1' => 'new'));
+        $this->assertEquals(array('foo1' => 'new'), $this->options->all());
+    }
+
+    public function testClearOptionAndNormalizer()
+    {
+        $this->options->set('foo1', 'bar');
+        $this->options->setNormalizer('foo1', function (Options $options) {
+            return '';
+        });
+        $this->options->set('foo2', 'bar');
+        $this->options->setNormalizer('foo2', function (Options $options) {
+            return '';
+        });
+
+        $this->options->clear();
+        $this->assertEmpty($this->options->all());
+    }
+
+    public function testNormalizerWithoutCorrespondingOption()
+    {
+        $test = $this;
+
+        $this->options->setNormalizer('foo', function (Options $options, $previousValue) use ($test) {
+            $test->assertNull($previousValue);
+
+            return '';
+        });
+        $this->assertEquals(array('foo' => ''), $this->options->all());
+    }
+}
diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/composer.json b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/composer.json
new file mode 100644 (file)
index 0000000..f13d246
--- /dev/null
@@ -0,0 +1,31 @@
+{
+    "name": "symfony/options-resolver",
+    "type": "library",
+    "description": "Symfony OptionsResolver Component",
+    "keywords": ["options", "config", "configuration"],
+    "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\\OptionsResolver\\": "" }
+    },
+    "target-dir": "Symfony/Component/OptionsResolver",
+    "minimum-stability": "dev",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "2.3-dev"
+        }
+    }
+}
diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/phpunit.xml.dist b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/phpunit.xml.dist
new file mode 100644 (file)
index 0000000..ad24d17
--- /dev/null
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         processIsolation="false"
+         stopOnFailure="false"
+         syntaxCheck="false"
+         bootstrap="vendor/autoload.php"
+>
+    <testsuites>
+        <testsuite name="Symfony OptionsResolver Component Test Suite">
+            <directory>./Tests/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./</directory>
+            <exclude>
+                <directory>./Resources</directory>
+                <directory>./Tests</directory>
+            </exclude>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/.gitattributes b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/.gitattributes
new file mode 100644 (file)
index 0000000..8048151
--- /dev/null
@@ -0,0 +1,2 @@
+/Tests export-ignore
+phpunit.xml.dist export-ignore
diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/.gitignore b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/.gitignore
new file mode 100644 (file)
index 0000000..44de97a
--- /dev/null
@@ -0,0 +1,4 @@
+vendor/
+composer.lock
+phpunit.xml
+
diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/CHANGELOG.md b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/CHANGELOG.md
new file mode 100644 (file)
index 0000000..071ef3b
--- /dev/null
@@ -0,0 +1,14 @@
+CHANGELOG
+=========
+
+2.3.0
+------
+
+ * added PropertyAccessorBuilder, to enable or disable the support of "__call"
+ * added support for "__call" in the PropertyAccessor (disabled by default)
+ * [BC BREAK] changed PropertyAccessor to continue its search for a property or
+   method even if a non-public match was found. Before, a PropertyAccessDeniedException
+   was thrown in this case. Class PropertyAccessDeniedException was removed
+   now.
+ * deprecated PropertyAccess::getPropertyAccessor
+ * added PropertyAccess::createPropertyAccessor and PropertyAccess::createPropertyAccessorBuilder
diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/ExceptionInterface.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/ExceptionInterface.php
new file mode 100644 (file)
index 0000000..d1fcdac
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess\Exception;
+
+/**
+ * Marker interface for the PropertyAccess component.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+interface ExceptionInterface
+{
+}
diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/InvalidPropertyPathException.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/InvalidPropertyPathException.php
new file mode 100644 (file)
index 0000000..69de31c
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess\Exception;
+
+/**
+ * Thrown when a property path is malformed.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class InvalidPropertyPathException extends RuntimeException
+{
+}
diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/NoSuchPropertyException.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/NoSuchPropertyException.php
new file mode 100644 (file)
index 0000000..ebaa5a3
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess\Exception;
+
+/**
+ * Thrown when a property cannot be found.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class NoSuchPropertyException extends RuntimeException
+{
+}
diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/OutOfBoundsException.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/OutOfBoundsException.php
new file mode 100644 (file)
index 0000000..a3c4559
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess\Exception;
+
+/**
+ * Base OutOfBoundsException for the PropertyAccess component.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class OutOfBoundsException extends \OutOfBoundsException implements ExceptionInterface
+{
+}
diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/RuntimeException.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/RuntimeException.php
new file mode 100644 (file)
index 0000000..9fe843e
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess\Exception;
+
+/**
+ * Base RuntimeException for the PropertyAccess component.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class RuntimeException extends \RuntimeException implements ExceptionInterface
+{
+}
diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.php
new file mode 100644 (file)
index 0000000..029d48c
--- /dev/null
@@ -0,0 +1,25 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess\Exception;
+
+/**
+ * Thrown when a value does not match an expected type.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class UnexpectedTypeException extends RuntimeException
+{
+    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/property-access/Symfony/Component/PropertyAccess/LICENSE b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/LICENSE
new file mode 100644 (file)
index 0000000..88a57f8
--- /dev/null
@@ -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/property-access/Symfony/Component/PropertyAccess/PropertyAccess.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccess.php
new file mode 100644 (file)
index 0000000..3b234df
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess;
+
+/**
+ * Entry point of the PropertyAccess component.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+final class PropertyAccess
+{
+    /**
+     * Creates a property accessor with the default configuration.
+     *
+     * @return PropertyAccessor The new property accessor
+     */
+    public static function createPropertyAccessor()
+    {
+        return self::createPropertyAccessorBuilder()->getPropertyAccessor();
+    }
+
+    /**
+     * Creates a property accessor builder.
+     *
+     * @return PropertyAccessorBuilder The new property accessor builder
+     */
+    public static function createPropertyAccessorBuilder()
+    {
+        return new PropertyAccessorBuilder();
+    }
+
+    /**
+     * Alias of {@link getPropertyAccessor}.
+     *
+     * @return PropertyAccessor The new property accessor
+     *
+     * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use
+     *             {@link createPropertyAccessor()} instead.
+     */
+    public static function getPropertyAccessor()
+    {
+        return self::createPropertyAccessor();
+    }
+
+    /**
+     * This class cannot be instantiated.
+     */
+    private function __construct()
+    {
+    }
+}
diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessor.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessor.php
new file mode 100644 (file)
index 0000000..c65d919
--- /dev/null
@@ -0,0 +1,442 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess;
+
+use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
+use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;
+
+/**
+ * Default implementation of {@link PropertyAccessorInterface}.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class PropertyAccessor implements PropertyAccessorInterface
+{
+    const VALUE = 0;
+    const IS_REF = 1;
+
+    private $magicCall;
+
+    /**
+     * Should not be used by application code. Use
+     * {@link PropertyAccess::getPropertyAccessor()} instead.
+     */
+    public function __construct($magicCall = false)
+    {
+        $this->magicCall = $magicCall;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getValue($objectOrArray, $propertyPath)
+    {
+        if (is_string($propertyPath)) {
+            $propertyPath = new PropertyPath($propertyPath);
+        } elseif (!$propertyPath instanceof PropertyPathInterface) {
+            throw new UnexpectedTypeException($propertyPath, 'string or Symfony\Component\PropertyAccess\PropertyPathInterface');
+        }
+
+        $propertyValues =& $this->readPropertiesUntil($objectOrArray, $propertyPath, $propertyPath->getLength());
+
+        return $propertyValues[count($propertyValues) - 1][self::VALUE];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setValue(&$objectOrArray, $propertyPath, $value)
+    {
+        if (is_string($propertyPath)) {
+            $propertyPath = new PropertyPath($propertyPath);
+        } elseif (!$propertyPath instanceof PropertyPathInterface) {
+            throw new UnexpectedTypeException($propertyPath, 'string or Symfony\Component\PropertyAccess\PropertyPathInterface');
+        }
+
+        $propertyValues =& $this->readPropertiesUntil($objectOrArray, $propertyPath, $propertyPath->getLength() - 1);
+        $overwrite = true;
+
+        // Add the root object to the list
+        array_unshift($propertyValues, array(
+            self::VALUE => &$objectOrArray,
+            self::IS_REF => true,
+        ));
+
+        for ($i = count($propertyValues) - 1; $i >= 0; --$i) {
+            $objectOrArray =& $propertyValues[$i][self::VALUE];
+
+            if ($overwrite) {
+                if (!is_object($objectOrArray) && !is_array($objectOrArray)) {
+                    throw new UnexpectedTypeException($objectOrArray, 'object or array');
+                }
+
+                $property = $propertyPath->getElement($i);
+                //$singular = $propertyPath->singulars[$i];
+                $singular = null;
+
+                if ($propertyPath->isIndex($i)) {
+                    $this->writeIndex($objectOrArray, $property, $value);
+                } else {
+                    $this->writeProperty($objectOrArray, $property, $singular, $value);
+                }
+            }
+
+            $value =& $objectOrArray;
+            $overwrite = !$propertyValues[$i][self::IS_REF];
+        }
+    }
+
+    /**
+     * Reads the path from an object up to a given path index.
+     *
+     * @param object|array          $objectOrArray The object or array to read from
+     * @param PropertyPathInterface $propertyPath  The property path to read
+     * @param integer               $lastIndex     The index up to which should be read
+     *
+     * @return array The values read in the path.
+     *
+     * @throws UnexpectedTypeException If a value within the path is neither object nor array.
+     */
+    private function &readPropertiesUntil(&$objectOrArray, PropertyPathInterface $propertyPath, $lastIndex)
+    {
+        $propertyValues = array();
+
+        for ($i = 0; $i < $lastIndex; ++$i) {
+            if (!is_object($objectOrArray) && !is_array($objectOrArray)) {
+                throw new UnexpectedTypeException($objectOrArray, 'object or array');
+            }
+
+            $property = $propertyPath->getElement($i);
+            $isIndex = $propertyPath->isIndex($i);
+            $isArrayAccess = is_array($objectOrArray) || $objectOrArray instanceof \ArrayAccess;
+
+            // Create missing nested arrays on demand
+            if ($isIndex && $isArrayAccess && !isset($objectOrArray[$property])) {
+                $objectOrArray[$property] = $i + 1 < $propertyPath->getLength() ? array() : null;
+            }
+
+            if ($isIndex) {
+                $propertyValue =& $this->readIndex($objectOrArray, $property);
+            } else {
+                $propertyValue =& $this->readProperty($objectOrArray, $property);
+            }
+
+            $objectOrArray =& $propertyValue[self::VALUE];
+
+            $propertyValues[] =& $propertyValue;
+        }
+
+        return $propertyValues;
+    }
+
+    /**
+     * Reads a key from an array-like structure.
+     *
+     * @param \ArrayAccess|array $array The array or \ArrayAccess object to read from
+     * @param string|integer     $index The key to read
+     *
+     * @return mixed The value of the key
+     *
+     * @throws NoSuchPropertyException If the array does not implement \ArrayAccess or it is not an array
+     */
+    private function &readIndex(&$array, $index)
+    {
+        if (!$array instanceof \ArrayAccess && !is_array($array)) {
+            throw new NoSuchPropertyException(sprintf('Index "%s" cannot be read from object of type "%s" because it doesn\'t implement \ArrayAccess', $index, get_class($array)));
+        }
+
+        // Use an array instead of an object since performance is very crucial here
+        $result = array(
+            self::VALUE => null,
+            self::IS_REF => false
+        );
+
+        if (isset($array[$index])) {
+            if (is_array($array)) {
+                $result[self::VALUE] =& $array[$index];
+                $result[self::IS_REF] = true;
+            } else {
+                $result[self::VALUE] = $array[$index];
+                // Objects are always passed around by reference
+                $result[self::IS_REF] = is_object($array[$index]) ? true : false;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Reads the a property from an object or array.
+     *
+     * @param object $object   The object to read from.
+     * @param string $property The property to read.
+     *
+     * @return mixed The value of the read property
+     *
+     * @throws NoSuchPropertyException If the property does not exist or is not
+     *                                 public.
+     */
+    private function &readProperty(&$object, $property)
+    {
+        // Use an array instead of an object since performance is
+        // very crucial here
+        $result = array(
+            self::VALUE => null,
+            self::IS_REF => false
+        );
+
+        if (!is_object($object)) {
+            throw new NoSuchPropertyException(sprintf('Cannot read property "%s" from an array. Maybe you should write the property path as "[%s]" instead?', $property, $property));
+        }
+
+        $camelProp = $this->camelize($property);
+        $reflClass = new \ReflectionClass($object);
+        $getter = 'get'.$camelProp;
+        $isser = 'is'.$camelProp;
+        $hasser = 'has'.$camelProp;
+        $classHasProperty = $reflClass->hasProperty($property);
+
+        if ($reflClass->hasMethod($getter) && $reflClass->getMethod($getter)->isPublic()) {
+            $result[self::VALUE] = $object->$getter();
+        } elseif ($reflClass->hasMethod($isser) && $reflClass->getMethod($isser)->isPublic()) {
+            $result[self::VALUE] = $object->$isser();
+        } elseif ($reflClass->hasMethod($hasser) && $reflClass->getMethod($hasser)->isPublic()) {
+            $result[self::VALUE] = $object->$hasser();
+        } elseif ($reflClass->hasMethod('__get') && $reflClass->getMethod('__get')->isPublic()) {
+            $result[self::VALUE] = $object->$property;
+        } elseif ($classHasProperty && $reflClass->getProperty($property)->isPublic()) {
+            $result[self::VALUE] =& $object->$property;
+            $result[self::IS_REF] = true;
+        } elseif (!$classHasProperty && property_exists($object, $property)) {
+            // Needed to support \stdClass instances. We need to explicitly
+            // exclude $classHasProperty, otherwise if in the previous clause
+            // a *protected* property was found on the class, property_exists()
+            // returns true, consequently the following line will result in a
+            // fatal error.
+            $result[self::VALUE] =& $object->$property;
+            $result[self::IS_REF] = true;
+        } elseif ($this->magicCall && $reflClass->hasMethod('__call') && $reflClass->getMethod('__call')->isPublic()) {
+            // we call the getter and hope the __call do the job
+            $result[self::VALUE] = $object->$getter();
+        } else {
+            throw new NoSuchPropertyException(sprintf(
+                'Neither the property "%s" nor one of the methods "%s()", '.
+                '"%s()", "%s()", "__get()" or "__call()" exist and have public access in '.
+                'class "%s".',
+                $property,
+                $getter,
+                $isser,
+                $hasser,
+                $reflClass->name
+            ));
+        }
+
+        // Objects are always passed around by reference
+        if (is_object($result[self::VALUE])) {
+            $result[self::IS_REF] = true;
+        }
+
+        return $result;
+    }
+
+    /**
+     * Sets the value of the property at the given index in the path
+     *
+     * @param \ArrayAccess|array $array An array or \ArrayAccess object to write to
+     * @param string|integer     $index The index to write at
+     * @param mixed              $value The value to write
+     *
+     * @throws NoSuchPropertyException If the array does not implement \ArrayAccess or it is not an array
+     */
+    private function writeIndex(&$array, $index, $value)
+    {
+        if (!$array instanceof \ArrayAccess && !is_array($array)) {
+            throw new NoSuchPropertyException(sprintf('Index "%s" cannot be modified in object of type "%s" because it doesn\'t implement \ArrayAccess', $index, get_class($array)));
+        }
+
+        $array[$index] = $value;
+    }
+
+    /**
+     * Sets the value of the property at the given index in the path
+     *
+     * @param object|array $object   The object or array to write to
+     * @param string       $property The property to write
+     * @param string|null  $singular The singular form of the property name or null
+     * @param mixed        $value    The value to write
+     *
+     * @throws NoSuchPropertyException If the property does not exist or is not
+     *                                 public.
+     */
+    private function writeProperty(&$object, $property, $singular, $value)
+    {
+        $guessedAdders = '';
+
+        if (!is_object($object)) {
+            throw new NoSuchPropertyException(sprintf('Cannot write property "%s" to an array. Maybe you should write the property path as "[%s]" instead?', $property, $property));
+        }
+
+        $reflClass = new \ReflectionClass($object);
+        $plural = $this->camelize($property);
+
+        // Any of the two methods is required, but not yet known
+        $singulars = null !== $singular ? array($singular) : (array) StringUtil::singularify($plural);
+
+        if (is_array($value) || $value instanceof \Traversable) {
+            $methods = $this->findAdderAndRemover($reflClass, $singulars);
+
+            if (null !== $methods) {
+                // At this point the add and remove methods have been found
+                // Use iterator_to_array() instead of clone in order to prevent side effects
+                // see https://github.com/symfony/symfony/issues/4670
+                $itemsToAdd = is_object($value) ? iterator_to_array($value) : $value;
+                $itemToRemove = array();
+                $propertyValue = $this->readProperty($object, $property);
+                $previousValue = $propertyValue[self::VALUE];
+
+                if (is_array($previousValue) || $previousValue instanceof \Traversable) {
+                    foreach ($previousValue as $previousItem) {
+                        foreach ($value as $key => $item) {
+                            if ($item === $previousItem) {
+                                // Item found, don't add
+                                unset($itemsToAdd[$key]);
+
+                                // Next $previousItem
+                                continue 2;
+                            }
+                        }
+
+                        // Item not found, add to remove list
+                        $itemToRemove[] = $previousItem;
+                    }
+                }
+
+                foreach ($itemToRemove as $item) {
+                    call_user_func(array($object, $methods[1]), $item);
+                }
+
+                foreach ($itemsToAdd as $item) {
+                    call_user_func(array($object, $methods[0]), $item);
+                }
+
+                return;
+            } else {
+                // It is sufficient to include only the adders in the error
+                // message. If the user implements the adder but not the remover,
+                // an exception will be thrown in findAdderAndRemover() that
+                // the remover has to be implemented as well.
+                $guessedAdders = '"add'.implode('()", "add', $singulars).'()", ';
+            }
+        }
+
+        $setter = 'set'.$this->camelize($property);
+        $classHasProperty = $reflClass->hasProperty($property);
+
+        if ($reflClass->hasMethod($setter) && $reflClass->getMethod($setter)->isPublic()) {
+            $object->$setter($value);
+        } elseif ($reflClass->hasMethod('__set') && $reflClass->getMethod('__set')->isPublic()) {
+            $object->$property = $value;
+        } elseif ($classHasProperty && $reflClass->getProperty($property)->isPublic()) {
+            $object->$property = $value;
+        } elseif (!$classHasProperty && property_exists($object, $property)) {
+            // Needed to support \stdClass instances. We need to explicitly
+            // exclude $classHasProperty, otherwise if in the previous clause
+            // a *protected* property was found on the class, property_exists()
+            // returns true, consequently the following line will result in a
+            // fatal error.
+            $object->$property = $value;
+        } elseif ($this->magicCall && $reflClass->hasMethod('__call') && $reflClass->getMethod('__call')->isPublic()) {
+            // we call the getter and hope the __call do the job
+            $object->$setter($value);
+        } else {
+            throw new NoSuchPropertyException(sprintf(
+                'Neither the property "%s" nor one of the methods %s"%s()", '.
+                '"__set()" or "__call()" exist and have public access in class "%s".',
+                $property,
+                $guessedAdders,
+                $setter,
+                $reflClass->name
+            ));
+        }
+    }
+
+    /**
+     * Camelizes a given string.
+     *
+     * @param  string $string Some string
+     *
+     * @return string The camelized version of the string
+     */
+    private function camelize($string)
+    {
+        return preg_replace_callback('/(^|_|\.)+(.)/', function ($match) { return ('.' === $match[1] ? '_' : '').strtoupper($match[2]); }, $string);
+    }
+
+    /**
+     * Searches for add and remove methods.
+     *
+     * @param \ReflectionClass $reflClass The reflection class for the given object
+     * @param array            $singulars The singular form of the property name or null
+     *
+     * @return array|null An array containing the adder and remover when found, null otherwise
+     *
+     * @throws NoSuchPropertyException If the property does not exist
+     */
+    private function findAdderAndRemover(\ReflectionClass $reflClass, array $singulars)
+    {
+        foreach ($singulars as $singular) {
+            $addMethod = 'add'.$singular;
+            $removeMethod = 'remove'.$singular;
+
+            $addMethodFound = $this->isAccessible($reflClass, $addMethod, 1);
+            $removeMethodFound = $this->isAccessible($reflClass, $removeMethod, 1);
+
+            if ($addMethodFound && $removeMethodFound) {
+                return array($addMethod, $removeMethod);
+            }
+
+            if ($addMethodFound xor $removeMethodFound) {
+                throw new NoSuchPropertyException(sprintf(
+                    'Found the public method "%s()", but did not find a public "%s()" on class %s',
+                    $addMethodFound ? $addMethod : $removeMethod,
+                    $addMethodFound ? $removeMethod : $addMethod,
+                    $reflClass->name
+                ));
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns whether a method is public and has a specific number of required parameters.
+     *
+     * @param  \ReflectionClass $class      The class of the method
+     * @param  string           $methodName The method name
+     * @param  integer          $parameters The number of parameters
+     *
+     * @return Boolean Whether the method is public and has $parameters
+     *                                      required parameters
+     */
+    private function isAccessible(\ReflectionClass $class, $methodName, $parameters)
+    {
+        if ($class->hasMethod($methodName)) {
+            $method = $class->getMethod($methodName);
+
+            if ($method->isPublic() && $method->getNumberOfRequiredParameters() === $parameters) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessorBuilder.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessorBuilder.php
new file mode 100644 (file)
index 0000000..25686e9
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess;
+
+/**
+ * A configurable builder for PropertyAccessorInterface objects.
+ *
+ * @author Jérémie Augustin <jeremie.augustin@pixel-cookers.com>
+ */
+class PropertyAccessorBuilder
+{
+    /**
+     * @var Boolean
+     */
+    private $magicCall = false;
+
+    /**
+     * Enables the use of "__call" by the ProperyAccessor.
+     *
+     * @return PropertyAccessorBuilder The builder object
+     */
+    public function enableMagicCall()
+    {
+        $this->magicCall = true;
+
+        return $this;
+    }
+
+    /**
+     * Disables the use of "__call" by the ProperyAccessor.
+     *
+     * @return PropertyAccessorBuilder The builder object
+     */
+    public function disableMagicCall()
+    {
+        $this->magicCall = false;
+
+        return $this;
+    }
+
+    /**
+     * @return Boolean true if the use of "__call" by the ProperyAccessor is enabled
+     */
+    public function isMagicCallEnabled()
+    {
+        return $this->magicCall;
+    }
+
+    /**
+     * Builds and returns a new propertyAccessor object.
+     *
+     * @return PropertyAccessorInterface The built propertyAccessor
+     */
+    public function getPropertyAccessor()
+    {
+        return new PropertyAccessor($this->magicCall);
+    }
+}
diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessorInterface.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessorInterface.php
new file mode 100644 (file)
index 0000000..1eed7c7
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess;
+
+/**
+ * Writes and reads values to/from an object/array graph.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+interface PropertyAccessorInterface
+{
+    /**
+     * Sets the value at the end of the property path of the object
+     *
+     * Example:
+     *
+     *     use Symfony\Component\PropertyAccess\PropertyAccess;
+     *
+     *     $propertyAccessor = PropertyAccess::getPropertyAccessor();
+     *
+     *     echo $propertyAccessor->setValue($object, 'child.name', 'Fabien');
+     *     // equals echo $object->getChild()->setName('Fabien');
+     *
+     * This method first tries to find a public setter for each property in the
+     * path. The name of the setter must be the camel-cased property name
+     * prefixed with "set".
+     *
+     * If the setter does not exist, this method tries to find a public
+     * property. The value of the property is then changed.
+     *
+     * If neither is found, an exception is thrown.
+     *
+     * @param object|array                 $objectOrArray The object or array to modify
+     * @param string|PropertyPathInterface $propertyPath  The property path to modify
+     * @param mixed                        $value         The value to set at the end of the property path
+     *
+     * @throws Exception\NoSuchPropertyException If a property does not exist or is not public.
+     * @throws Exception\UnexpectedTypeException If a value within the path is neither object
+     *                                           nor array
+     */
+    public function setValue(&$objectOrArray, $propertyPath, $value);
+
+    /**
+     * Returns the value at the end of the property path of the object
+     *
+     * Example:
+     *
+     *     use Symfony\Component\PropertyAccess\PropertyAccess;
+     *
+     *     $propertyAccessor = PropertyAccess::getPropertyAccessor();
+     *
+     *     echo $propertyAccessor->getValue($object, 'child.name);
+     *     // equals echo $object->getChild()->getName();
+     *
+     * This method first tries to find a public getter for each property in the
+     * path. The name of the getter must be the camel-cased property name
+     * prefixed with "get", "is", or "has".
+     *
+     * If the getter does not exist, this method tries to find a public
+     * property. The value of the property is then returned.
+     *
+     * If none of them are found, an exception is thrown.
+     *
+     * @param object|array                 $objectOrArray The object or array to traverse
+     * @param string|PropertyPathInterface $propertyPath  The property path to read
+     *
+     * @return mixed The value at the end of the property path
+     *
+     * @throws Exception\NoSuchPropertyException If a property does not exist or is not public.
+     */
+    public function getValue($objectOrArray, $propertyPath);
+}
diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPath.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPath.php
new file mode 100644 (file)
index 0000000..840fc71
--- /dev/null
@@ -0,0 +1,225 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess;
+
+use Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException;
+use Symfony\Component\PropertyAccess\Exception\OutOfBoundsException;
+use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;
+
+/**
+ * Default implementation of {@link PropertyPathInterface}.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class PropertyPath implements \IteratorAggregate, PropertyPathInterface
+{
+    /**
+     * Character used for separating between plural and singular of an element.
+     * @var string
+     */
+    const SINGULAR_SEPARATOR = '|';
+
+    /**
+     * The elements of the property path
+     * @var array
+     */
+    private $elements = array();
+
+    /**
+     * The singular forms of the elements in the property path.
+     * @var array
+     */
+    private $singulars = array();
+
+    /**
+     * The number of elements in the property path
+     * @var integer
+     */
+    private $length;
+
+    /**
+     * Contains a Boolean for each property in $elements denoting whether this
+     * element is an index. It is a property otherwise.
+     * @var array
+     */
+    private $isIndex = array();
+
+    /**
+     * String representation of the path
+     * @var string
+     */
+    private $pathAsString;
+
+    /**
+     * Constructs a property path from a string.
+     *
+     * @param PropertyPath|string $propertyPath The property path as string or instance
+     *
+     * @throws UnexpectedTypeException      If the given path is not a string
+     * @throws InvalidPropertyPathException If the syntax of the property path is not valid
+     */
+    public function __construct($propertyPath)
+    {
+        // Can be used as copy constructor
+        if ($propertyPath instanceof PropertyPath) {
+            /* @var PropertyPath $propertyPath */
+            $this->elements = $propertyPath->elements;
+            $this->singulars = $propertyPath->singulars;
+            $this->length = $propertyPath->length;
+            $this->isIndex = $propertyPath->isIndex;
+            $this->pathAsString = $propertyPath->pathAsString;
+
+            return;
+        }
+        if (!is_string($propertyPath)) {
+            throw new UnexpectedTypeException($propertyPath, 'string or Symfony\Component\PropertyAccess\PropertyPath');
+        }
+
+        if ('' === $propertyPath) {
+            throw new InvalidPropertyPathException('The property path should not be empty.');
+        }
+
+        $this->pathAsString = $propertyPath;
+        $position = 0;
+        $remaining = $propertyPath;
+
+        // first element is evaluated differently - no leading dot for properties
+        $pattern = '/^(([^\.\[]+)|\[([^\]]+)\])(.*)/';
+
+        while (preg_match($pattern, $remaining, $matches)) {
+            if ('' !== $matches[2]) {
+                $element = $matches[2];
+                $this->isIndex[] = false;
+            } else {
+                $element = $matches[3];
+                $this->isIndex[] = true;
+            }
+            // Disabled this behaviour as the syntax is not yet final
+            //$pos = strpos($element, self::SINGULAR_SEPARATOR);
+            $pos = false;
+            $singular = null;
+
+            if (false !== $pos) {
+                $singular = substr($element, $pos + 1);
+                $element = substr($element, 0, $pos);
+            }
+
+            $this->elements[] = $element;
+            $this->singulars[] = $singular;
+
+            $position += strlen($matches[1]);
+            $remaining = $matches[4];
+            $pattern = '/^(\.(\w+)|\[([^\]]+)\])(.*)/';
+        }
+
+        if ('' !== $remaining) {
+            throw new InvalidPropertyPathException(sprintf(
+                'Could not parse property path "%s". Unexpected token "%s" at position %d',
+                $propertyPath,
+                $remaining{0},
+                $position
+            ));
+        }
+
+        $this->length = count($this->elements);
+    }
+
+    /**
+     * {@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;
+        $parent->pathAsString = substr($parent->pathAsString, 0, max(strrpos($parent->pathAsString, '.'), strrpos($parent->pathAsString, '[')));
+        array_pop($parent->elements);
+        array_pop($parent->singulars);
+        array_pop($parent->isIndex);
+
+        return $parent;
+    }
+
+    /**
+     * Returns a new iterator for this path
+     *
+     * @return PropertyPathIteratorInterface
+     */
+    public function getIterator()
+    {
+        return new PropertyPathIterator($this);
+    }
+
+    /**
+     * {@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 property 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 property 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 property path', $index));
+        }
+
+        return $this->isIndex[$index];
+    }
+}
diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathBuilder.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathBuilder.php
new file mode 100644 (file)
index 0000000..f4eb0fb
--- /dev/null
@@ -0,0 +1,306 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess;
+
+use Symfony\Component\PropertyAccess\Exception\OutOfBoundsException;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class PropertyPathBuilder
+{
+    /**
+     * @var array
+     */
+    private $elements = array();
+
+    /**
+     * @var array
+     */
+    private $isIndex = array();
+
+    /**
+     * Creates a new property path builder.
+     *
+     * @param null|PropertyPathInterface|string $path The path to initially store
+     *                                                in the builder. Optional.
+     */
+    public function __construct($path = null)
+    {
+        if (null !== $path) {
+            $this->append($path);
+        }
+    }
+
+    /**
+     * Appends a (sub-) path to the current path.
+     *
+     * @param PropertyPathInterface|string $path   The path to append.
+     * @param integer                      $offset The offset where the appended
+     *                                             piece starts in $path.
+     * @param integer                      $length The length of the appended piece.
+     *                                             If 0, the full path is appended.
+     */
+    public function append($path, $offset = 0, $length = 0)
+    {
+        if (is_string($path)) {
+            $path = new PropertyPath($path);
+        }
+
+        if (0 === $length) {
+            $end = $path->getLength();
+        } else {
+            $end = $offset + $length;
+        }
+
+        for (; $offset < $end; ++$offset) {
+            $this->elements[] = $path->getElement($offset);
+            $this->isIndex[] = $path->isIndex($offset);
+        }
+    }
+
+    /**
+     * Appends an index element to the current path.
+     *
+     * @param string $name The name of the appended index
+     */
+    public function appendIndex($name)
+    {
+        $this->elements[] = $name;
+        $this->isIndex[] = true;
+    }
+
+    /**
+     * Appends a property element to the current path.
+     *
+     * @param string $name The name of the appended property
+     */
+    public function appendProperty($name)
+    {
+        $this->elements[] = $name;
+        $this->isIndex[] = false;
+    }
+
+    /**
+     * Removes elements from the current path.
+     *
+     * @param integer $offset The offset at which to remove
+     * @param integer $length The length of the removed piece
+     *
+     * @throws OutOfBoundsException if offset is invalid
+     */
+    public function remove($offset, $length = 1)
+    {
+        if (!isset($this->elements[$offset])) {
+            throw new OutOfBoundsException(sprintf('The offset %s is not within the property path', $offset));
+        }
+
+        $this->resize($offset, $length, 0);
+    }
+
+    /**
+     * Replaces a sub-path by a different (sub-) path.
+     *
+     * @param integer                      $offset     The offset at which to replace.
+     * @param integer                      $length     The length of the piece to replace.
+     * @param PropertyPathInterface|string $path       The path to insert.
+     * @param integer                      $pathOffset The offset where the inserted piece
+     *                                                 starts in $path.
+     * @param integer                      $pathLength The length of the inserted piece.
+     *                                                 If 0, the full path is inserted.
+     *
+     * @throws OutOfBoundsException If the offset is invalid
+     */
+    public function replace($offset, $length, $path, $pathOffset = 0, $pathLength = 0)
+    {
+        if (is_string($path)) {
+            $path = new PropertyPath($path);
+        }
+
+        if ($offset < 0 && abs($offset) <= $this->getLength()) {
+            $offset = $this->getLength() + $offset;
+        } elseif (!isset($this->elements[$offset])) {
+            throw new OutOfBoundsException('The offset ' . $offset . ' is not within the property path');
+        }
+
+        if (0 === $pathLength) {
+            $pathLength = $path->getLength() - $pathOffset;
+        }
+
+        $this->resize($offset, $length, $pathLength);
+
+        for ($i = 0; $i < $pathLength; ++$i) {
+            $this->elements[$offset + $i] = $path->getElement($pathOffset + $i);
+            $this->isIndex[$offset + $i] = $path->isIndex($pathOffset + $i);
+        }
+    }
+
+    /**
+     * Replaces a property element by an index element.
+     *
+     * @param integer $offset The offset at which to replace
+     * @param string  $name   The new name of the element. Optional.
+     *
+     * @throws OutOfBoundsException If the offset is invalid
+     */
+    public function replaceByIndex($offset, $name = null)
+    {
+        if (!isset($this->elements[$offset])) {
+            throw new OutOfBoundsException(sprintf('The offset %s is not within the property path', $offset));
+        }
+
+        if (null !== $name) {
+            $this->elements[$offset] = $name;
+        }
+
+        $this->isIndex[$offset] = true;
+    }
+
+    /**
+     * Replaces an index element by a property element.
+     *
+     * @param integer $offset The offset at which to replace
+     * @param string  $name   The new name of the element. Optional.
+     *
+     * @throws OutOfBoundsException If the offset is invalid
+     */
+    public function replaceByProperty($offset, $name = null)
+    {
+        if (!isset($this->elements[$offset])) {
+            throw new OutOfBoundsException(sprintf('The offset %s is not within the property path', $offset));
+        }
+
+        if (null !== $name) {
+            $this->elements[$offset] = $name;
+        }
+
+        $this->isIndex[$offset] = false;
+    }
+
+    /**
+     * Returns the length of the current path.
+     *
+     * @return integer The path length
+     */
+    public function getLength()
+    {
+        return count($this->elements);
+    }
+
+    /**
+     * Returns the current property path.
+     *
+     * @return PropertyPathInterface The constructed property path
+     */
+    public function getPropertyPath()
+    {
+        $pathAsString = $this->__toString();
+
+        return '' !== $pathAsString ? new PropertyPath($pathAsString) : null;
+    }
+
+    /**
+     * Returns the current property path as string.
+     *
+     * @return string The property path as string
+     */
+    public function __toString()
+    {
+        $string = '';
+
+        foreach ($this->elements as $offset => $element) {
+            if ($this->isIndex[$offset]) {
+                $element = '['.$element.']';
+            } elseif ('' !== $string) {
+                $string .= '.';
+            }
+
+            $string .= $element;
+        }
+
+        return $string;
+    }
+
+    /**
+     * Resizes the path so that a chunk of length $cutLength is
+     * removed at $offset and another chunk of length $insertionLength
+     * can be inserted.
+     *
+     * @param  integer $offset          The offset where the removed chunk starts
+     * @param  integer $cutLength       The length of the removed chunk
+     * @param  integer $insertionLength The length of the inserted chunk
+     */
+    private function resize($offset, $cutLength, $insertionLength)
+    {
+        // Nothing else to do in this case
+        if ($insertionLength === $cutLength) {
+            return;
+        }
+
+        $length = count($this->elements);
+
+        if ($cutLength > $insertionLength) {
+            // More elements should be removed than inserted
+            $diff = $cutLength - $insertionLength;
+            $newLength = $length - $diff;
+
+            // Shift elements to the left (left-to-right until the new end)
+            // Max allowed offset to be shifted is such that
+            // $offset + $diff < $length (otherwise invalid index access)
+            // i.e. $offset < $length - $diff = $newLength
+            for ($i = $offset; $i < $newLength; ++$i) {
+                $this->elements[$i] = $this->elements[$i + $diff];
+                $this->isIndex[$i] = $this->isIndex[$i + $diff];
+            }
+
+            // All remaining elements should be removed
+            for (; $i < $length; ++$i) {
+                unset($this->elements[$i]);
+                unset($this->isIndex[$i]);
+            }
+        } else {
+            $diff = $insertionLength - $cutLength;
+
+            $newLength = $length + $diff;
+            $indexAfterInsertion = $offset + $insertionLength;
+
+            // $diff <= $insertionLength
+            // $indexAfterInsertion >= $insertionLength
+            // => $diff <= $indexAfterInsertion
+
+            // In each of the following loops, $i >= $diff must hold,
+            // otherwise ($i - $diff) becomes negative.
+
+            // Shift old elements to the right to make up space for the
+            // inserted elements. This needs to be done left-to-right in
+            // order to preserve an ascending array index order
+            // Since $i = max($length, $indexAfterInsertion) and $indexAfterInsertion >= $diff,
+            // $i >= $diff is guaranteed.
+            for ($i = max($length, $indexAfterInsertion); $i < $newLength; ++$i) {
+                $this->elements[$i] = $this->elements[$i - $diff];
+                $this->isIndex[$i] = $this->isIndex[$i - $diff];
+            }
+
+            // Shift remaining elements to the right. Do this right-to-left
+            // so we don't overwrite elements before copying them
+            // The last written index is the immediate index after the inserted
+            // string, because the indices before that will be overwritten
+            // anyway.
+            // Since $i >= $indexAfterInsertion and $indexAfterInsertion >= $diff,
+            // $i >= $diff is guaranteed.
+            for ($i = $length - 1; $i >= $indexAfterInsertion; --$i) {
+                $this->elements[$i] = $this->elements[$i - $diff];
+                $this->isIndex[$i] = $this->isIndex[$i - $diff];
+            }
+        }
+    }
+}
diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathInterface.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathInterface.php
new file mode 100644 (file)
index 0000000..95f34ff
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess;
+
+/**
+ * A sequence of property names or array indices.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+interface PropertyPathInterface extends \Traversable
+{
+    /**
+     * Returns the string representation of the property path
+     *
+     * @return string The path as string
+     */
+    public function __toString();
+
+    /**
+     * Returns the length of the property path, i.e. the number of elements.
+     *
+     * @return integer The path length
+     */
+    public function getLength();
+
+    /**
+     * Returns the parent property path.
+     *
+     * The parent property path is the one that contains the same items as
+     * this one except for the last one.
+     *
+     * If this property path only contains one item, null is returned.
+     *
+     * @return PropertyPath The parent path or null
+     */
+    public function getParent();
+
+    /**
+     * Returns the elements of the property path as array
+     *
+     * @return array An array of property/index names
+     */
+    public function getElements();
+
+    /**
+     * Returns the element at the given index in the property path
+     *
+     * @param  integer $index The index key
+     *
+     * @return string A property or index name
+     *
+     * @throws Exception\OutOfBoundsException If the offset is invalid
+     */
+    public function getElement($index);
+
+    /**
+     * Returns whether the element at the given index is a property
+     *
+     * @param  integer $index The index in the property path
+     *
+     * @return Boolean Whether the element at this index is a property
+     *
+     * @throws Exception\OutOfBoundsException If the offset is invalid
+     */
+    public function isProperty($index);
+
+    /**
+     * Returns whether the element at the given index is an array index
+     *
+     * @param  integer $index The index in the property path
+     *
+     * @return Boolean Whether the element at this index is an array index
+     *
+     * @throws Exception\OutOfBoundsException If the offset is invalid
+     */
+    public function isIndex($index);
+}
diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathIterator.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathIterator.php
new file mode 100644 (file)
index 0000000..d6cd49c
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess;
+
+/**
+ * Traverses a property path and provides additional methods to find out
+ * information about the current element
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class PropertyPathIterator extends \ArrayIterator implements PropertyPathIteratorInterface
+{
+    /**
+     * The traversed property path
+     * @var PropertyPathInterface
+     */
+    protected $path;
+
+    /**
+     * Constructor.
+     *
+     * @param PropertyPathInterface $path The property path to traverse
+     */
+    public function __construct(PropertyPathInterface $path)
+    {
+        parent::__construct($path->getElements());
+
+        $this->path = $path;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isIndex()
+    {
+        return $this->path->isIndex($this->key());
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isProperty()
+    {
+        return $this->path->isProperty($this->key());
+    }
+}
diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathIteratorInterface.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathIteratorInterface.php
new file mode 100644 (file)
index 0000000..cb43f8d
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+interface PropertyPathIteratorInterface extends \Iterator, \SeekableIterator
+{
+    /**
+     * Returns whether the current element in the property path is an array
+     * index.
+     *
+     * @return Boolean
+     */
+    public function isIndex();
+
+    /**
+     * Returns whether the current element in the property path is a property
+     * name.
+     *
+     * @return Boolean
+     */
+    public function isProperty();
+}
diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/README.md b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/README.md
new file mode 100644 (file)
index 0000000..0ae94e0
--- /dev/null
@@ -0,0 +1,14 @@
+PropertyAccess Component
+========================
+
+PropertyAccess reads/writes values from/to object/array graphs using a simple
+string notation.
+
+Resources
+---------
+
+You can run the unit tests with the following command:
+
+    $ cd path/to/Symfony/Component/PropertyAccess/
+    $ composer.phar install --dev
+    $ phpunit
diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/StringUtil.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/StringUtil.php
new file mode 100644 (file)
index 0000000..e782cc1
--- /dev/null
@@ -0,0 +1,195 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess;
+
+/**
+ * Creates singulars from plurals.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class StringUtil
+{
+    /**
+     * Map english plural to singular suffixes
+     *
+     * @var array
+     *
+     * @see http://english-zone.com/spelling/plurals.html
+     * @see http://www.scribd.com/doc/3271143/List-of-100-Irregular-Plural-Nouns-in-English
+     */
+    private static $pluralMap = array(
+        // First entry: plural suffix, reversed
+        // Second entry: length of plural suffix
+        // Third entry: Whether the suffix may succeed a vocal
+        // Fourth entry: Whether the suffix may succeed a consonant
+        // Fifth entry: singular suffix, normal
+
+        // bacteria (bacterium), criteria (criterion), phenomena (phenomenon)
+        array('a', 1, true, true, array('on', 'um')),
+
+        // nebulae (nebula)
+        array('ea', 2, true, true, 'a'),
+
+        // mice (mouse), lice (louse)
+        array('eci', 3, false, true, 'ouse'),
+
+        // geese (goose)
+        array('esee', 4, false, true, 'oose'),
+
+        // fungi (fungus), alumni (alumnus), syllabi (syllabus), radii (radius)
+        array('i', 1, true, true, 'us'),
+
+        // men (man), women (woman)
+        array('nem', 3, true, true, 'man'),
+
+        // children (child)
+        array('nerdlihc', 8, true, true, 'child'),
+
+        // oxen (ox)
+        array('nexo', 4, false, false, 'ox'),
+
+        // indices (index), appendices (appendix), prices (price)
+        array('seci', 4, false, true, array('ex', 'ix', 'ice')),
+
+        // babies (baby)
+        array('sei', 3, false, true, 'y'),
+
+        // analyses (analysis), ellipses (ellipsis), funguses (fungus),
+        // neuroses (neurosis), theses (thesis), emphases (emphasis),
+        // oases (oasis), crises (crisis), houses (house), bases (base),
+        // atlases (atlas), kisses (kiss)
+        array('ses', 3, true, true, array('s', 'se', 'sis')),
+
+        // objectives (objective), alternative (alternatives)
+        array('sevit', 5, true, true, 'tive'),
+
+        // lives (life), wives (wife)
+        array('sevi', 4, false, true, 'ife'),
+
+        // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf)
+        array('sev', 3, true, true, 'f'),
+
+        // axes (axis), axes (ax), axes (axe)
+        array('sexa', 4, false, false, array('ax', 'axe', 'axis')),
+
+        // indexes (index), matrixes (matrix)
+        array('sex', 3, true, false, 'x'),
+
+        // quizzes (quiz)
+        array('sezz', 4, true, false, 'z'),
+
+        // bureaus (bureau)
+        array('suae', 4, false, true, 'eau'),
+
+        // roses (rose), garages (garage), cassettes (cassette),
+        // waltzes (waltz), heroes (hero), bushes (bush), arches (arch),
+        // shoes (shoe)
+        array('se', 2, true, true, array('', 'e')),
+
+        // tags (tag)
+        array('s', 1, true, true, ''),
+
+        // chateaux (chateau)
+        array('xuae', 4, false, true, 'eau'),
+    );
+
+    /**
+     * This class should not be instantiated
+     */
+    private function __construct() {}
+
+    /**
+     * Returns the singular form of a word
+     *
+     * If the method can't determine the form with certainty, an array of the
+     * possible singulars is returned.
+     *
+     * @param string $plural A word in plural form
+     * @return string|array The singular form or an array of possible singular
+     *                      forms
+     */
+    public static function singularify($plural)
+    {
+        $pluralRev = strrev($plural);
+        $lowerPluralRev = strtolower($pluralRev);
+        $pluralLength = strlen($lowerPluralRev);
+
+        // The outer loop iterates over the entries of the plural table
+        // The inner loop $j iterates over the characters of the plural suffix
+        // in the plural table to compare them with the characters of the actual
+        // given plural suffix
+        foreach (self::$pluralMap as $map) {
+            $suffix = $map[0];
+            $suffixLength = $map[1];
+            $j = 0;
+
+            // Compare characters in the plural table and of the suffix of the
+            // given plural one by one
+            while ($suffix[$j] === $lowerPluralRev[$j]) {
+                // Let $j point to the next character
+                ++$j;
+
+                // Successfully compared the last character
+                // Add an entry with the singular suffix to the singular array
+                if ($j === $suffixLength) {
+                    // Is there any character preceding the suffix in the plural string?
+                    if ($j < $pluralLength) {
+                        $nextIsVocal = false !== strpos('aeiou', $lowerPluralRev[$j]);
+
+                        if (!$map[2] && $nextIsVocal) {
+                            // suffix may not succeed a vocal but next char is one
+                            break;
+                        }
+
+                        if (!$map[3] && !$nextIsVocal) {
+                            // suffix may not succeed a consonant but next char is one
+                            break;
+                        }
+                    }
+
+                    $newBase = substr($plural, 0, $pluralLength - $suffixLength);
+                    $newSuffix = $map[4];
+
+                    // Check whether the first character in the plural suffix
+                    // is uppercased. If yes, uppercase the first character in
+                    // the singular suffix too
+                    $firstUpper = ctype_upper($pluralRev[$j - 1]);
+
+                    if (is_array($newSuffix)) {
+                        $singulars = array();
+
+                        foreach ($newSuffix as $newSuffixEntry) {
+                            $singulars[] = $newBase.($firstUpper ? ucfirst($newSuffixEntry) : $newSuffixEntry);
+                        }
+
+                        return $singulars;
+                    }
+
+                    return $newBase.($firstUpper ? ucFirst($newSuffix) : $newSuffix);
+                }
+
+                // Suffix is longer than word
+                if ($j === $pluralLength) {
+                    break;
+                }
+            }
+        }
+
+        // Convert teeth to tooth, feet to foot
+        if (false !== ($pos = strpos($plural, 'ee'))) {
+            return substr_replace($plural, 'oo', $pos, 2);
+        }
+
+        // Assume that plural and singular is identical
+        return $plural;
+    }
+}
diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/composer.json b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/composer.json
new file mode 100644 (file)
index 0000000..3186042
--- /dev/null
@@ -0,0 +1,31 @@
+{
+    "name": "symfony/property-access",
+    "type": "library",
+    "description": "Symfony PropertyAccess Component",
+    "keywords": ["property", "index", "access", "object", "array", "extraction", "injection", "reflection", "property path"],
+    "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\\PropertyAccess\\": "" }
+    },
+    "target-dir": "Symfony/Component/PropertyAccess",
+    "minimum-stability": "dev",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "2.3-dev"
+        }
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/.gitignore b/vendor/symfony/routing/Symfony/Component/Routing/.gitignore
new file mode 100644 (file)
index 0000000..44de97a
--- /dev/null
@@ -0,0 +1,4 @@
+vendor/
+composer.lock
+phpunit.xml
+
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Annotation/Route.php b/vendor/symfony/routing/Symfony/Component/Routing/Annotation/Route.php
new file mode 100644 (file)
index 0000000..abdbea2
--- /dev/null
@@ -0,0 +1,156 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Annotation;
+
+/**
+ * Annotation class for @Route().
+ *
+ * @Annotation
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class Route
+{
+    private $path;
+    private $name;
+    private $requirements;
+    private $options;
+    private $defaults;
+    private $host;
+    private $methods;
+    private $schemes;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data An array of key/value parameters.
+     *
+     * @throws \BadMethodCallException
+     */
+    public function __construct(array $data)
+    {
+        $this->requirements = array();
+        $this->options = array();
+        $this->defaults = array();
+        $this->methods = array();
+        $this->schemes = array();
+
+        if (isset($data['value'])) {
+            $data['path'] = $data['value'];
+            unset($data['value']);
+        }
+
+        foreach ($data as $key => $value) {
+            $method = 'set'.str_replace('_', '', $key);
+            if (!method_exists($this, $method)) {
+                throw new \BadMethodCallException(sprintf("Unknown property '%s' on annotation '%s'.", $key, get_class($this)));
+            }
+            $this->$method($value);
+        }
+    }
+
+    /**
+     * @deprecated Deprecated in 2.2, to be removed in 3.0. Use setPath instead.
+     */
+    public function setPattern($pattern)
+    {
+        $this->path = $pattern;
+    }
+
+    /**
+     * @deprecated Deprecated in 2.2, to be removed in 3.0. Use getPath instead.
+     */
+    public function getPattern()
+    {
+        return $this->path;
+    }
+
+    public function setPath($path)
+    {
+        $this->path = $path;
+    }
+
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    public function setHost($pattern)
+    {
+        $this->host = $pattern;
+    }
+
+    public function getHost()
+    {
+        return $this->host;
+    }
+
+    public function setName($name)
+    {
+        $this->name = $name;
+    }
+
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function setRequirements($requirements)
+    {
+        $this->requirements = $requirements;
+    }
+
+    public function getRequirements()
+    {
+        return $this->requirements;
+    }
+
+    public function setOptions($options)
+    {
+        $this->options = $options;
+    }
+
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
+    public function setDefaults($defaults)
+    {
+        $this->defaults = $defaults;
+    }
+
+    public function getDefaults()
+    {
+        return $this->defaults;
+    }
+
+    public function setSchemes($schemes)
+    {
+        $this->schemes = is_array($schemes) ? $schemes : array($schemes);
+    }
+
+    public function getSchemes()
+    {
+        return $this->schemes;
+    }
+
+    public function setMethods($methods)
+    {
+        $this->methods = is_array($methods) ? $methods : array($methods);
+    }
+
+    public function getMethods()
+    {
+        return $this->methods;
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/CHANGELOG.md b/vendor/symfony/routing/Symfony/Component/Routing/CHANGELOG.md
new file mode 100644 (file)
index 0000000..f0c616d
--- /dev/null
@@ -0,0 +1,162 @@
+CHANGELOG
+=========
+
+2.3.0
+-----
+
+ * added RequestContext::getQueryString()
+
+2.2.0
+-----
+
+ * [DEPRECATION] Several route settings have been renamed (the old ones will be removed in 3.0):
+
+    * The `pattern` setting for a route has been deprecated in favor of `path`
+    * The `_scheme` and `_method` requirements have been moved to the `schemes` and `methods` settings
+
+   Before:
+
+   ```
+   article_edit:
+       pattern: /article/{id}
+       requirements: { '_method': 'POST|PUT', '_scheme': 'https', 'id': '\d+' }
+
+   <route id="article_edit" pattern="/article/{id}">
+       <requirement key="_method">POST|PUT</requirement>
+       <requirement key="_scheme">https</requirement>
+       <requirement key="id">\d+</requirement>
+   </route>
+
+   $route = new Route();
+   $route->setPattern('/article/{id}');
+   $route->setRequirement('_method', 'POST|PUT');
+   $route->setRequirement('_scheme', 'https');
+   ```
+
+   After:
+
+   ```
+   article_edit:
+       path: /article/{id}
+       methods: [POST, PUT]
+       schemes: https
+       requirements: { 'id': '\d+' }
+
+   <route id="article_edit" pattern="/article/{id}" methods="POST PUT" schemes="https">
+       <requirement key="id">\d+</requirement>
+   </route>
+
+   $route = new Route();
+   $route->setPath('/article/{id}');
+   $route->setMethods(array('POST', 'PUT'));
+   $route->setSchemes('https');
+   ```
+
+ * [BC BREAK] RouteCollection does not behave like a tree structure anymore but as
+   a flat array of Routes. So when using PHP to build the RouteCollection, you must
+   make sure to add routes to the sub-collection before adding it to the parent
+   collection (this is not relevant when using YAML or XML for Route definitions).
+
+   Before:
+
+   ```
+   $rootCollection = new RouteCollection();
+   $subCollection = new RouteCollection();
+   $rootCollection->addCollection($subCollection);
+   $subCollection->add('foo', new Route('/foo'));
+   ```
+
+   After:
+
+   ```
+   $rootCollection = new RouteCollection();
+   $subCollection = new RouteCollection();
+   $subCollection->add('foo', new Route('/foo'));
+   $rootCollection->addCollection($subCollection);
+   ```
+
+   Also one must call `addCollection` from the bottom to the top hierarchy.
+   So the correct sequence is the following (and not the reverse):
+
+   ```
+   $childCollection->->addCollection($grandchildCollection);
+   $rootCollection->addCollection($childCollection);
+   ```
+
+ * [DEPRECATION] The methods `RouteCollection::getParent()` and `RouteCollection::getRoot()`
+   have been deprecated and will be removed in Symfony 2.3.
+ * [BC BREAK] Misusing the `RouteCollection::addPrefix` method to add defaults, requirements
+   or options without adding a prefix is not supported anymore. So if you called `addPrefix`
+   with an empty prefix or `/` only (both have no relevance), like
+   `addPrefix('', $defaultsArray, $requirementsArray, $optionsArray)`
+   you need to use the new dedicated methods `addDefaults($defaultsArray)`,
+   `addRequirements($requirementsArray)` or `addOptions($optionsArray)` instead.
+ * [DEPRECATION] The `$options` parameter to `RouteCollection::addPrefix()` has been deprecated
+   because adding options has nothing to do with adding a path prefix. If you want to add options
+   to all child routes of a RouteCollection, you can use `addOptions()`.
+ * [DEPRECATION] The method `RouteCollection::getPrefix()` has been deprecated
+   because it suggested that all routes in the collection would have this prefix, which is
+   not necessarily true. On top of that, since there is no tree structure anymore, this method
+   is also useless. Don't worry about performance, prefix optimization for matching is still done
+   in the dumper, which was also improved in 2.2.0 to find even more grouping possibilities.
+ * [DEPRECATION] `RouteCollection::addCollection(RouteCollection $collection)` should now only be
+   used with a single parameter. The other params `$prefix`, `$default`, `$requirements` and `$options`
+   will still work, but have been deprecated. The `addPrefix` method should be used for this
+   use-case instead.
+   Before: `$parentCollection->addCollection($collection, '/prefix', array(...), array(...))`
+   After:
+   ```
+   $collection->addPrefix('/prefix', array(...), array(...));
+   $parentCollection->addCollection($collection);
+   ```
+ * added support for the method default argument values when defining a @Route
+ * Adjacent placeholders without separator work now, e.g. `/{x}{y}{z}.{_format}`.
+ * Characters that function as separator between placeholders are now whitelisted
+   to fix routes with normal text around a variable, e.g. `/prefix{var}suffix`.
+ * [BC BREAK] The default requirement of a variable has been changed slightly.
+   Previously it disallowed the previous and the next char around a variable. Now
+   it disallows the slash (`/`) and the next char. Using the previous char added
+   no value and was problematic because the route `/index.{_format}` would be
+   matched by `/index.ht/ml`.
+ * The default requirement now uses possessive quantifiers when possible which
+   improves matching performance by up to 20% because it prevents backtracking
+   when it's not needed.
+ * The ConfigurableRequirementsInterface can now also be used to disable the requirements
+   check on URL generation completely by calling `setStrictRequirements(null)`. It
+   improves performance in production environment as you should know that params always
+   pass the requirements (otherwise it would break your link anyway).
+ * There is no restriction on the route name anymore. So non-alphanumeric characters
+   are now also allowed.
+ * [BC BREAK] `RouteCompilerInterface::compile(Route $route)` was made static
+   (only relevant if you implemented your own RouteCompiler).
+ * Added possibility to generate relative paths and network paths in the UrlGenerator, e.g.
+   "../parent-file" and "//example.com/dir/file". The third parameter in
+   `UrlGeneratorInterface::generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH)`
+   now accepts more values and you should use the constants defined in `UrlGeneratorInterface` for
+   claritiy. The old method calls with a Boolean parameter will continue to work because they
+   equal the signature using the constants.
+
+2.1.0
+-----
+
+ * added RequestMatcherInterface
+ * added RequestContext::fromRequest()
+ * the UrlMatcher does not throw a \LogicException anymore when the required
+   scheme is not the current one
+ * added TraceableUrlMatcher
+ * added the possibility to define options, default values and requirements
+   for placeholders in prefix, including imported routes
+ * added RouterInterface::getRouteCollection
+ * [BC BREAK] the UrlMatcher urldecodes the route parameters only once, they
+   were decoded twice before. Note that the `urldecode()` calls have been
+   changed for a single `rawurldecode()` in order to support `+` for input
+   paths.
+ * added RouteCollection::getRoot method to retrieve the root of a
+   RouteCollection tree
+ * [BC BREAK] made RouteCollection::setParent private which could not have
+   been used anyway without creating inconsistencies
+ * [BC BREAK] RouteCollection::remove also removes a route from parent
+   collections (not only from its children)
+ * added ConfigurableRequirementsInterface that allows to disable exceptions 
+   (and generate empty URLs instead) when generating a route with an invalid
+   parameter value
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/CompiledRoute.php b/vendor/symfony/routing/Symfony/Component/Routing/CompiledRoute.php
new file mode 100644 (file)
index 0000000..7878455
--- /dev/null
@@ -0,0 +1,134 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing;
+
+/**
+ * CompiledRoutes are returned by the RouteCompiler class.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class CompiledRoute
+{
+    private $variables;
+    private $tokens;
+    private $staticPrefix;
+    private $regex;
+    private $pathVariables;
+    private $hostVariables;
+    private $hostRegex;
+    private $hostTokens;
+
+    /**
+     * Constructor.
+     *
+     * @param string      $staticPrefix       The static prefix of the compiled route
+     * @param string      $regex              The regular expression to use to match this route
+     * @param array       $tokens             An array of tokens to use to generate URL for this route
+     * @param array       $pathVariables      An array of path variables
+     * @param string|null $hostRegex          Host regex
+     * @param array       $hostTokens         Host tokens
+     * @param array       $hostVariables      An array of host variables
+     * @param array       $variables          An array of variables (variables defined in the path and in the host patterns)
+     */
+    public function __construct($staticPrefix, $regex, array $tokens, array $pathVariables, $hostRegex = null, array $hostTokens = array(), array $hostVariables = array(), array $variables = array())
+    {
+        $this->staticPrefix = (string) $staticPrefix;
+        $this->regex = $regex;
+        $this->tokens = $tokens;
+        $this->pathVariables = $pathVariables;
+        $this->hostRegex = $hostRegex;
+        $this->hostTokens = $hostTokens;
+        $this->hostVariables = $hostVariables;
+        $this->variables = $variables;
+    }
+
+    /**
+     * Returns the static prefix.
+     *
+     * @return string The static prefix
+     */
+    public function getStaticPrefix()
+    {
+        return $this->staticPrefix;
+    }
+
+    /**
+     * Returns the regex.
+     *
+     * @return string The regex
+     */
+    public function getRegex()
+    {
+        return $this->regex;
+    }
+
+    /**
+     * Returns the host regex
+     *
+     * @return string|null The host regex or null
+     */
+    public function getHostRegex()
+    {
+        return $this->hostRegex;
+    }
+
+    /**
+     * Returns the tokens.
+     *
+     * @return array The tokens
+     */
+    public function getTokens()
+    {
+        return $this->tokens;
+    }
+
+    /**
+     * Returns the host tokens.
+     *
+     * @return array The tokens
+     */
+    public function getHostTokens()
+    {
+        return $this->hostTokens;
+    }
+
+    /**
+     * Returns the variables.
+     *
+     * @return array The variables
+     */
+    public function getVariables()
+    {
+        return $this->variables;
+    }
+
+    /**
+     * Returns the path variables.
+     *
+     * @return array The variables
+     */
+    public function getPathVariables()
+    {
+        return $this->pathVariables;
+    }
+
+    /**
+     * Returns the host variables.
+     *
+     * @return array The variables
+     */
+    public function getHostVariables()
+    {
+        return $this->hostVariables;
+    }
+
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Exception/ExceptionInterface.php b/vendor/symfony/routing/Symfony/Component/Routing/Exception/ExceptionInterface.php
new file mode 100644 (file)
index 0000000..5289f52
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Exception;
+
+/**
+ * ExceptionInterface
+ *
+ * @author Alexandre Salomé <alexandre.salome@gmail.com>
+ *
+ * @api
+ */
+interface ExceptionInterface
+{
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Exception/InvalidParameterException.php b/vendor/symfony/routing/Symfony/Component/Routing/Exception/InvalidParameterException.php
new file mode 100644 (file)
index 0000000..4f12469
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Exception;
+
+/**
+ * Exception thrown when a parameter is not valid
+ *
+ * @author Alexandre Salomé <alexandre.salome@gmail.com>
+ *
+ * @api
+ */
+class InvalidParameterException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Exception/MethodNotAllowedException.php b/vendor/symfony/routing/Symfony/Component/Routing/Exception/MethodNotAllowedException.php
new file mode 100644 (file)
index 0000000..32f1091
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Exception;
+
+/**
+ * The resource was found but the request method is not allowed.
+ *
+ * This exception should trigger an HTTP 405 response in your application code.
+ *
+ * @author Kris Wallsmith <kris@symfony.com>
+ *
+ * @api
+ */
+class MethodNotAllowedException extends \RuntimeException implements ExceptionInterface
+{
+    /**
+     * @var array
+     */
+    protected $allowedMethods = array();
+
+    public function __construct(array $allowedMethods, $message = null, $code = 0, \Exception $previous = null)
+    {
+        $this->allowedMethods = array_map('strtoupper', $allowedMethods);
+
+        parent::__construct($message, $code, $previous);
+    }
+
+    /**
+     * Gets the allowed HTTP methods.
+     *
+     * @return array
+     */
+    public function getAllowedMethods()
+    {
+        return $this->allowedMethods;
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Exception/MissingMandatoryParametersException.php b/vendor/symfony/routing/Symfony/Component/Routing/Exception/MissingMandatoryParametersException.php
new file mode 100644 (file)
index 0000000..5a523fa
--- /dev/null
@@ -0,0 +1,24 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Exception;
+
+/**
+ * Exception thrown when a route cannot be generated because of missing
+ * mandatory parameters.
+ *
+ * @author Alexandre Salomé <alexandre.salome@gmail.com>
+ *
+ * @api
+ */
+class MissingMandatoryParametersException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Exception/ResourceNotFoundException.php b/vendor/symfony/routing/Symfony/Component/Routing/Exception/ResourceNotFoundException.php
new file mode 100644 (file)
index 0000000..362a0d6
--- /dev/null
@@ -0,0 +1,25 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Exception;
+
+/**
+ * The resource was not found.
+ *
+ * This exception should trigger an HTTP 404 response in your application code.
+ *
+ * @author Kris Wallsmith <kris@symfony.com>
+ *
+ * @api
+ */
+class ResourceNotFoundException extends \RuntimeException implements ExceptionInterface
+{
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Exception/RouteNotFoundException.php b/vendor/symfony/routing/Symfony/Component/Routing/Exception/RouteNotFoundException.php
new file mode 100644 (file)
index 0000000..4d5f288
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Exception;
+
+/**
+ * Exception thrown when a route does not exists
+ *
+ * @author Alexandre Salomé <alexandre.salome@gmail.com>
+ *
+ * @api
+ */
+class RouteNotFoundException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Generator/ConfigurableRequirementsInterface.php b/vendor/symfony/routing/Symfony/Component/Routing/Generator/ConfigurableRequirementsInterface.php
new file mode 100644 (file)
index 0000000..5925838
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Generator;
+
+/**
+ * ConfigurableRequirementsInterface must be implemented by URL generators that
+ * can be configured whether an exception should be generated when the parameters
+ * do not match the requirements. It is also possible to disable the requirements
+ * check for URL generation completely.
+ *
+ * The possible configurations and use-cases:
+ * - setStrictRequirements(true): Throw an exception for mismatching requirements. This
+ *   is mostly useful in development environment.
+ * - setStrictRequirements(false): Don't throw an exception but return null as URL for
+ *   mismatching requirements and log the problem. Useful when you cannot control all
+ *   params because they come from third party libs but don't want to have a 404 in
+ *   production environment. It should log the mismatch so one can review it.
+ * - setStrictRequirements(null): Return the URL with the given parameters without
+ *   checking the requirements at all. When generating an URL you should either trust
+ *   your params or you validated them beforehand because otherwise it would break your
+ *   link anyway. So in production environment you should know that params always pass
+ *   the requirements. Thus this option allows to disable the check on URL generation for
+ *   performance reasons (saving a preg_match for each requirement every time a URL is
+ *   generated).
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Tobias Schultze <http://tobion.de>
+ */
+interface ConfigurableRequirementsInterface
+{
+    /**
+     * Enables or disables the exception on incorrect parameters.
+     * Passing null will deactivate the requirements check completely.
+     *
+     * @param Boolean|null $enabled
+     */
+    public function setStrictRequirements($enabled);
+
+    /**
+     * Returns whether to throw an exception on incorrect parameters.
+     * Null means the requirements check is deactivated completely.
+     *
+     * @return Boolean|null
+     */
+    public function isStrictRequirements();
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Generator/Dumper/GeneratorDumper.php b/vendor/symfony/routing/Symfony/Component/Routing/Generator/Dumper/GeneratorDumper.php
new file mode 100644 (file)
index 0000000..4739bd8
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Generator\Dumper;
+
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * GeneratorDumper is the base class for all built-in generator dumpers.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+abstract class GeneratorDumper implements GeneratorDumperInterface
+{
+    /**
+     * @var RouteCollection
+     */
+    private $routes;
+
+    /**
+     * Constructor.
+     *
+     * @param RouteCollection $routes The RouteCollection to dump
+     */
+    public function __construct(RouteCollection $routes)
+    {
+        $this->routes = $routes;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getRoutes()
+    {
+        return $this->routes;
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Generator/Dumper/GeneratorDumperInterface.php b/vendor/symfony/routing/Symfony/Component/Routing/Generator/Dumper/GeneratorDumperInterface.php
new file mode 100644 (file)
index 0000000..deb0c0a
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Generator\Dumper;
+
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * GeneratorDumperInterface is the interface that all generator dumper classes must implement.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+interface GeneratorDumperInterface
+{
+    /**
+     * Dumps a set of routes to a string representation of executable code
+     * that can then be used to generate a URL of such a route.
+     *
+     * @param array $options An array of options
+     *
+     * @return string Executable code
+     */
+    public function dump(array $options = array());
+
+    /**
+     * Gets the routes to dump.
+     *
+     * @return RouteCollection A RouteCollection instance
+     */
+    public function getRoutes();
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php b/vendor/symfony/routing/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php
new file mode 100644 (file)
index 0000000..42cd910
--- /dev/null
@@ -0,0 +1,123 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Generator\Dumper;
+
+/**
+ * PhpGeneratorDumper creates a PHP class able to generate URLs for a given set of routes.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Tobias Schultze <http://tobion.de>
+ *
+ * @api
+ */
+class PhpGeneratorDumper extends GeneratorDumper
+{
+    /**
+     * Dumps a set of routes to a PHP class.
+     *
+     * Available options:
+     *
+     *  * class:      The class name
+     *  * base_class: The base class name
+     *
+     * @param array $options An array of options
+     *
+     * @return string A PHP class representing the generator class
+     *
+     * @api
+     */
+    public function dump(array $options = array())
+    {
+        $options = array_merge(array(
+            'class'      => 'ProjectUrlGenerator',
+            'base_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
+        ), $options);
+
+        return <<<EOF
+<?php
+
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\Exception\RouteNotFoundException;
+use Psr\Log\LoggerInterface;
+
+/**
+ * {$options['class']}
+ *
+ * This class has been auto-generated
+ * by the Symfony Routing Component.
+ */
+class {$options['class']} extends {$options['base_class']}
+{
+    static private \$declaredRoutes = {$this->generateDeclaredRoutes()};
+
+    /**
+     * Constructor.
+     */
+    public function __construct(RequestContext \$context, LoggerInterface \$logger = null)
+    {
+        \$this->context = \$context;
+        \$this->logger = \$logger;
+    }
+
+{$this->generateGenerateMethod()}
+}
+
+EOF;
+    }
+
+    /**
+     * Generates PHP code representing an array of defined routes
+     * together with the routes properties (e.g. requirements).
+     *
+     * @return string PHP code
+     */
+    private function generateDeclaredRoutes()
+    {
+        $routes = "array(\n";
+        foreach ($this->getRoutes()->all() as $name => $route) {
+            $compiledRoute = $route->compile();
+
+            $properties = array();
+            $properties[] = $compiledRoute->getVariables();
+            $properties[] = $route->getDefaults();
+            $properties[] = $route->getRequirements();
+            $properties[] = $compiledRoute->getTokens();
+            $properties[] = $compiledRoute->getHostTokens();
+
+            $routes .= sprintf("        '%s' => %s,\n", $name, str_replace("\n", '', var_export($properties, true)));
+        }
+        $routes .= '    )';
+
+        return $routes;
+    }
+
+    /**
+     * Generates PHP code representing the `generate` method that implements the UrlGeneratorInterface.
+     *
+     * @return string PHP code
+     */
+    private function generateGenerateMethod()
+    {
+        return <<<EOF
+    public function generate(\$name, \$parameters = array(), \$referenceType = self::ABSOLUTE_PATH)
+    {
+        if (!isset(self::\$declaredRoutes[\$name])) {
+            throw new RouteNotFoundException(sprintf('Unable to generate a URL for the named route "%s" as such route does not exist.', \$name));
+        }
+
+        list(\$variables, \$defaults, \$requirements, \$tokens, \$hostTokens) = self::\$declaredRoutes[\$name];
+
+        return \$this->doGenerate(\$variables, \$defaults, \$requirements, \$tokens, \$parameters, \$name, \$referenceType, \$hostTokens);
+    }
+EOF;
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Generator/UrlGenerator.php b/vendor/symfony/routing/Symfony/Component/Routing/Generator/UrlGenerator.php
new file mode 100644 (file)
index 0000000..f224cb3
--- /dev/null
@@ -0,0 +1,322 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Generator;
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\Exception\InvalidParameterException;
+use Symfony\Component\Routing\Exception\RouteNotFoundException;
+use Symfony\Component\Routing\Exception\MissingMandatoryParametersException;
+use Psr\Log\LoggerInterface;
+
+/**
+ * UrlGenerator can generate a URL or a path for any route in the RouteCollection
+ * based on the passed parameters.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Tobias Schultze <http://tobion.de>
+ *
+ * @api
+ */
+class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInterface
+{
+    /**
+     * @var RouteCollection
+     */
+    protected $routes;
+
+    /**
+     * @var RequestContext
+     */
+    protected $context;
+
+    /**
+     * @var Boolean|null
+     */
+    protected $strictRequirements = true;
+
+    /**
+     * @var LoggerInterface|null
+     */
+    protected $logger;
+
+    /**
+     * This array defines the characters (besides alphanumeric ones) that will not be percent-encoded in the path segment of the generated URL.
+     *
+     * PHP's rawurlencode() encodes all chars except "a-zA-Z0-9-._~" according to RFC 3986. But we want to allow some chars
+     * to be used in their literal form (reasons below). Other chars inside the path must of course be encoded, e.g.
+     * "?" and "#" (would be interpreted wrongly as query and fragment identifier),
+     * "'" and """ (are used as delimiters in HTML).
+     */
+    protected $decodedChars = array(
+        // the slash can be used to designate a hierarchical structure and we want allow using it with this meaning
+        // some webservers don't allow the slash in encoded form in the path for security reasons anyway
+        // see http://stackoverflow.com/questions/4069002/http-400-if-2f-part-of-get-url-in-jboss
+        '%2F' => '/',
+        // the following chars are general delimiters in the URI specification but have only special meaning in the authority component
+        // so they can safely be used in the path in unencoded form
+        '%40' => '@',
+        '%3A' => ':',
+        // these chars are only sub-delimiters that have no predefined meaning and can therefore be used literally
+        // so URI producing applications can use these chars to delimit subcomponents in a path segment without being encoded for better readability
+        '%3B' => ';',
+        '%2C' => ',',
+        '%3D' => '=',
+        '%2B' => '+',
+        '%21' => '!',
+        '%2A' => '*',
+        '%7C' => '|',
+    );
+
+    /**
+     * Constructor.
+     *
+     * @param RouteCollection      $routes  A RouteCollection instance
+     * @param RequestContext       $context The context
+     * @param LoggerInterface|null $logger  A logger instance
+     *
+     * @api
+     */
+    public function __construct(RouteCollection $routes, RequestContext $context, LoggerInterface $logger = null)
+    {
+        $this->routes = $routes;
+        $this->context = $context;
+        $this->logger = $logger;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setContext(RequestContext $context)
+    {
+        $this->context = $context;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getContext()
+    {
+        return $this->context;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setStrictRequirements($enabled)
+    {
+        $this->strictRequirements = null === $enabled ? null : (Boolean) $enabled;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isStrictRequirements()
+    {
+        return $this->strictRequirements;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH)
+    {
+        if (null === $route = $this->routes->get($name)) {
+            throw new RouteNotFoundException(sprintf('Unable to generate a URL for the named route "%s" as such route does not exist.', $name));
+        }
+
+        // the Route has a cache of its own and is not recompiled as long as it does not get modified
+        $compiledRoute = $route->compile();
+
+        return $this->doGenerate($compiledRoute->getVariables(), $route->getDefaults(), $route->getRequirements(), $compiledRoute->getTokens(), $parameters, $name, $referenceType, $compiledRoute->getHostTokens());
+    }
+
+    /**
+     * @throws MissingMandatoryParametersException When some parameters are missing that are mandatory for the route
+     * @throws InvalidParameterException           When a parameter value for a placeholder is not correct because
+     *                                             it does not match the requirement
+     */
+    protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $referenceType, $hostTokens)
+    {
+        $variables = array_flip($variables);
+        $mergedParams = array_replace($defaults, $this->context->getParameters(), $parameters);
+
+        // all params must be given
+        if ($diff = array_diff_key($variables, $mergedParams)) {
+            throw new MissingMandatoryParametersException(sprintf('Some mandatory parameters are missing ("%s") to generate a URL for route "%s".', implode('", "', array_keys($diff)), $name));
+        }
+
+        $url = '';
+        $optional = true;
+        foreach ($tokens as $token) {
+            if ('variable' === $token[0]) {
+                if (!$optional || !array_key_exists($token[3], $defaults) || null !== $mergedParams[$token[3]] && (string) $mergedParams[$token[3]] !== (string) $defaults[$token[3]]) {
+                    // check requirement
+                    if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#', $mergedParams[$token[3]])) {
+                        $message = sprintf('Parameter "%s" for route "%s" must match "%s" ("%s" given) to generate a corresponding URL.', $token[3], $name, $token[2], $mergedParams[$token[3]]);
+                        if ($this->strictRequirements) {
+                            throw new InvalidParameterException($message);
+                        }
+
+                        if ($this->logger) {
+                            $this->logger->error($message);
+                        }
+
+                        return null;
+                    }
+
+                    $url = $token[1].$mergedParams[$token[3]].$url;
+                    $optional = false;
+                }
+            } else {
+                // static text
+                $url = $token[1].$url;
+                $optional = false;
+            }
+        }
+
+        if ('' === $url) {
+            $url = '/';
+        }
+
+        // the contexts base url is already encoded (see Symfony\Component\HttpFoundation\Request)
+        $url = strtr(rawurlencode($url), $this->decodedChars);
+
+        // the path segments "." and ".." are interpreted as relative reference when resolving a URI; see http://tools.ietf.org/html/rfc3986#section-3.3
+        // so we need to encode them as they are not used for this purpose here
+        // otherwise we would generate a URI that, when followed by a user agent (e.g. browser), does not match this route
+        $url = strtr($url, array('/../' => '/%2E%2E/', '/./' => '/%2E/'));
+        if ('/..' === substr($url, -3)) {
+            $url = substr($url, 0, -2).'%2E%2E';
+        } elseif ('/.' === substr($url, -2)) {
+            $url = substr($url, 0, -1).'%2E';
+        }
+
+        $schemeAuthority = '';
+        if ($host = $this->context->getHost()) {
+            $scheme = $this->context->getScheme();
+            if (isset($requirements['_scheme']) && ($req = strtolower($requirements['_scheme'])) && $scheme !== $req) {
+                $referenceType = self::ABSOLUTE_URL;
+                $scheme = $req;
+            }
+
+            if ($hostTokens) {
+                $routeHost = '';
+                foreach ($hostTokens as $token) {
+                    if ('variable' === $token[0]) {
+                        if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#', $mergedParams[$token[3]])) {
+                            $message = sprintf('Parameter "%s" for route "%s" must match "%s" ("%s" given) to generate a corresponding URL.', $token[3], $name, $token[2], $mergedParams[$token[3]]);
+
+                            if ($this->strictRequirements) {
+                                throw new InvalidParameterException($message);
+                            }
+
+                            if ($this->logger) {
+                                $this->logger->error($message);
+                            }
+
+                            return null;
+                        }
+
+                        $routeHost = $token[1].$mergedParams[$token[3]].$routeHost;
+                    } else {
+                        $routeHost = $token[1].$routeHost;
+                    }
+                }
+
+                if ($routeHost !== $host) {
+                    $host = $routeHost;
+                    if (self::ABSOLUTE_URL !== $referenceType) {
+                        $referenceType = self::NETWORK_PATH;
+                    }
+                }
+            }
+
+            if (self::ABSOLUTE_URL === $referenceType || self::NETWORK_PATH === $referenceType) {
+                $port = '';
+                if ('http' === $scheme && 80 != $this->context->getHttpPort()) {
+                    $port = ':'.$this->context->getHttpPort();
+                } elseif ('https' === $scheme && 443 != $this->context->getHttpsPort()) {
+                    $port = ':'.$this->context->getHttpsPort();
+                }
+
+                $schemeAuthority = self::NETWORK_PATH === $referenceType ? '//' : "$scheme://";
+                $schemeAuthority .= $host.$port;
+            }
+        }
+
+        if (self::RELATIVE_PATH === $referenceType) {
+            $url = self::getRelativePath($this->context->getPathInfo(), $url);
+        } else {
+            $url = $schemeAuthority.$this->context->getBaseUrl().$url;
+        }
+
+        // add a query string if needed
+        $extra = array_diff_key($parameters, $variables, $defaults);
+        if ($extra && $query = http_build_query($extra, '', '&')) {
+            $url .= '?'.$query;
+        }
+
+        return $url;
+    }
+
+    /**
+     * Returns the target path as relative reference from the base path.
+     *
+     * Only the URIs path component (no schema, host etc.) is relevant and must be given, starting with a slash.
+     * Both paths must be absolute and not contain relative parts.
+     * Relative URLs from one resource to another are useful when generating self-contained downloadable document archives.
+     * Furthermore, they can be used to reduce the link size in documents.
+     *
+     * Example target paths, given a base path of "/a/b/c/d":
+     * - "/a/b/c/d"     -> ""
+     * - "/a/b/c/"      -> "./"
+     * - "/a/b/"        -> "../"
+     * - "/a/b/c/other" -> "other"
+     * - "/a/x/y"       -> "../../x/y"
+     *
+     * @param string $basePath   The base path
+     * @param string $targetPath The target path
+     *
+     * @return string The relative target path
+     */
+    public static function getRelativePath($basePath, $targetPath)
+    {
+        if ($basePath === $targetPath) {
+            return '';
+        }
+
+        $sourceDirs = explode('/', isset($basePath[0]) && '/' === $basePath[0] ? substr($basePath, 1) : $basePath);
+        $targetDirs = explode('/', isset($targetPath[0]) && '/' === $targetPath[0] ? substr($targetPath, 1) : $targetPath);
+        array_pop($sourceDirs);
+        $targetFile = array_pop($targetDirs);
+
+        foreach ($sourceDirs as $i => $dir) {
+            if (isset($targetDirs[$i]) && $dir === $targetDirs[$i]) {
+                unset($sourceDirs[$i], $targetDirs[$i]);
+            } else {
+                break;
+            }
+        }
+
+        $targetDirs[] = $targetFile;
+        $path = str_repeat('../', count($sourceDirs)).implode('/', $targetDirs);
+
+        // A reference to the same base directory or an empty subdirectory must be prefixed with "./".
+        // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used
+        // as the first segment of a relative-path reference, as it would be mistaken for a scheme name
+        // (see http://tools.ietf.org/html/rfc3986#section-4.2).
+        return '' === $path || '/' === $path[0]
+            || false !== ($colonPos = strpos($path, ':')) && ($colonPos < ($slashPos = strpos($path, '/')) || false === $slashPos)
+            ? "./$path" : $path;
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Generator/UrlGeneratorInterface.php b/vendor/symfony/routing/Symfony/Component/Routing/Generator/UrlGeneratorInterface.php
new file mode 100644 (file)
index 0000000..8e3b277
--- /dev/null
@@ -0,0 +1,87 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Generator;
+
+use Symfony\Component\Routing\Exception\InvalidParameterException;
+use Symfony\Component\Routing\Exception\MissingMandatoryParametersException;
+use Symfony\Component\Routing\Exception\RouteNotFoundException;
+use Symfony\Component\Routing\RequestContextAwareInterface;
+
+/**
+ * UrlGeneratorInterface is the interface that all URL generator classes must implement.
+ *
+ * The constants in this interface define the different types of resource references that
+ * are declared in RFC 3986: http://tools.ietf.org/html/rfc3986
+ * We are using the term "URL" instead of "URI" as this is more common in web applications
+ * and we do not need to distinguish them as the difference is mostly semantical and
+ * less technical. Generating URIs, i.e. representation-independent resource identifiers,
+ * is also possible.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Tobias Schultze <http://tobion.de>
+ *
+ * @api
+ */
+interface UrlGeneratorInterface extends RequestContextAwareInterface
+{
+    /**
+     * Generates an absolute URL, e.g. "http://example.com/dir/file".
+     */
+    const ABSOLUTE_URL = true;
+
+    /**
+     * Generates an absolute path, e.g. "/dir/file".
+     */
+    const ABSOLUTE_PATH = false;
+
+    /**
+     * Generates a relative path based on the current request path, e.g. "../parent-file".
+     * @see UrlGenerator::getRelativePath()
+     */
+    const RELATIVE_PATH = 'relative';
+
+    /**
+     * Generates a network path, e.g. "//example.com/dir/file".
+     * Such reference reuses the current scheme but specifies the host.
+     */
+    const NETWORK_PATH = 'network';
+
+    /**
+     * Generates a URL or path for a specific route based on the given parameters.
+     *
+     * Parameters that reference placeholders in the route pattern will substitute them in the
+     * path or host. Extra params are added as query string to the URL.
+     *
+     * When the passed reference type cannot be generated for the route because it requires a different
+     * host or scheme than the current one, the method will return a more comprehensive reference
+     * that includes the required params. For example, when you call this method with $referenceType = ABSOLUTE_PATH
+     * but the route requires the https scheme whereas the current scheme is http, it will instead return an
+     * ABSOLUTE_URL with the https scheme and the current host. This makes sure the generated URL matches
+     * the route in any case.
+     *
+     * If there is no route with the given name, the generator must throw the RouteNotFoundException.
+     *
+     * @param string         $name          The name of the route
+     * @param mixed          $parameters    An array of parameters
+     * @param Boolean|string $referenceType The type of reference to be generated (one of the constants)
+     *
+     * @return string The generated URL
+     *
+     * @throws RouteNotFoundException              If the named route doesn't exist
+     * @throws MissingMandatoryParametersException When some parameters are missing that are mandatory for the route
+     * @throws InvalidParameterException           When a parameter value for a placeholder is not correct because
+     *                                             it does not match the requirement
+     *
+     * @api
+     */
+    public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH);
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/LICENSE b/vendor/symfony/routing/Symfony/Component/Routing/LICENSE
new file mode 100644 (file)
index 0000000..88a57f8
--- /dev/null
@@ -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/routing/Symfony/Component/Routing/Loader/AnnotationClassLoader.php b/vendor/symfony/routing/Symfony/Component/Routing/Loader/AnnotationClassLoader.php
new file mode 100644 (file)
index 0000000..9831d85
--- /dev/null
@@ -0,0 +1,246 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Loader;
+
+use Doctrine\Common\Annotations\Reader;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Config\Loader\LoaderInterface;
+use Symfony\Component\Config\Loader\LoaderResolverInterface;
+
+/**
+ * AnnotationClassLoader loads routing information from a PHP class and its methods.
+ *
+ * You need to define an implementation for the getRouteDefaults() method. Most of the
+ * time, this method should define some PHP callable to be called for the route
+ * (a controller in MVC speak).
+ *
+ * The @Route annotation can be set on the class (for global parameters),
+ * and on each method.
+ *
+ * The @Route annotation main value is the route path. The annotation also
+ * recognizes several parameters: requirements, options, defaults, schemes,
+ * methods, host, and name. The name parameter is mandatory.
+ * Here is an example of how you should be able to use it:
+ *
+ *     /**
+ *      * @Route("/Blog")
+ *      * /
+ *     class Blog
+ *     {
+ *         /**
+ *          * @Route("/", name="blog_index")
+ *          * /
+ *         public function index()
+ *         {
+ *         }
+ *
+ *         /**
+ *          * @Route("/{id}", name="blog_post", requirements = {"id" = "\d+"})
+ *          * /
+ *         public function show()
+ *         {
+ *         }
+ *     }
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+abstract class AnnotationClassLoader implements LoaderInterface
+{
+    /**
+     * @var Reader
+     */
+    protected $reader;
+
+    /**
+     * @var string
+     */
+    protected $routeAnnotationClass = 'Symfony\\Component\\Routing\\Annotation\\Route';
+
+    /**
+     * @var integer
+     */
+    protected $defaultRouteIndex = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param Reader $reader
+     */
+    public function __construct(Reader $reader)
+    {
+        $this->reader = $reader;
+    }
+
+    /**
+     * Sets the annotation class to read route properties from.
+     *
+     * @param string $class A fully-qualified class name
+     */
+    public function setRouteAnnotationClass($class)
+    {
+        $this->routeAnnotationClass = $class;
+    }
+
+    /**
+     * Loads from annotations from a class.
+     *
+     * @param string      $class A class name
+     * @param string|null $type  The resource type
+     *
+     * @return RouteCollection A RouteCollection instance
+     *
+     * @throws \InvalidArgumentException When route can't be parsed
+     */
+    public function load($class, $type = null)
+    {
+        if (!class_exists($class)) {
+            throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class));
+        }
+
+        $globals = array(
+            'path'         => '',
+            'requirements' => array(),
+            'options'      => array(),
+            'defaults'     => array(),
+            'schemes'      => array(),
+            'methods'      => array(),
+            'host'         => '',
+        );
+
+        $class = new \ReflectionClass($class);
+        if ($class->isAbstract()) {
+            throw new \InvalidArgumentException(sprintf('Annotations from class "%s" cannot be read as it is abstract.', $class));
+        }
+
+        if ($annot = $this->reader->getClassAnnotation($class, $this->routeAnnotationClass)) {
+            // for BC reasons
+            if (null !== $annot->getPath()) {
+                $globals['path'] = $annot->getPath();
+            } elseif (null !== $annot->getPattern()) {
+                $globals['path'] = $annot->getPattern();
+            }
+
+            if (null !== $annot->getRequirements()) {
+                $globals['requirements'] = $annot->getRequirements();
+            }
+
+            if (null !== $annot->getOptions()) {
+                $globals['options'] = $annot->getOptions();
+            }
+
+            if (null !== $annot->getDefaults()) {
+                $globals['defaults'] = $annot->getDefaults();
+            }
+
+            if (null !== $annot->getSchemes()) {
+                $globals['schemes'] = $annot->getSchemes();
+            }
+
+            if (null !== $annot->getMethods()) {
+                $globals['methods'] = $annot->getMethods();
+            }
+
+            if (null !== $annot->getHost()) {
+                $globals['host'] = $annot->getHost();
+            }
+        }
+
+        $collection = new RouteCollection();
+        $collection->addResource(new FileResource($class->getFileName()));
+
+        foreach ($class->getMethods() as $method) {
+            $this->defaultRouteIndex = 0;
+            foreach ($this->reader->getMethodAnnotations($method) as $annot) {
+                if ($annot instanceof $this->routeAnnotationClass) {
+                    $this->addRoute($collection, $annot, $globals, $class, $method);
+                }
+            }
+        }
+
+        return $collection;
+    }
+
+    protected function addRoute(RouteCollection $collection, $annot, $globals, \ReflectionClass $class, \ReflectionMethod $method)
+    {
+        $name = $annot->getName();
+        if (null === $name) {
+            $name = $this->getDefaultRouteName($class, $method);
+        }
+
+        $defaults = array_replace($globals['defaults'], $annot->getDefaults());
+        foreach ($method->getParameters() as $param) {
+            if ($param->isOptional()) {
+                $defaults[$param->getName()] = $param->getDefaultValue();
+            }
+        }
+        $requirements = array_replace($globals['requirements'], $annot->getRequirements());
+        $options = array_replace($globals['options'], $annot->getOptions());
+        $schemes = array_replace($globals['schemes'], $annot->getSchemes());
+        $methods = array_replace($globals['methods'], $annot->getMethods());
+
+        $host = $annot->getHost();
+        if (null === $host) {
+            $host = $globals['host'];
+        }
+
+        $route = new Route($globals['path'].$annot->getPath(), $defaults, $requirements, $options, $host, $schemes, $methods);
+
+        $this->configureRoute($route, $class, $method, $annot);
+
+        $collection->add($name, $route);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function supports($resource, $type = null)
+    {
+        return is_string($resource) && preg_match('/^(?:\\\\?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)+$/', $resource) && (!$type || 'annotation' === $type);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setResolver(LoaderResolverInterface $resolver)
+    {
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getResolver()
+    {
+    }
+
+    /**
+     * Gets the default route name for a class method.
+     *
+     * @param \ReflectionClass  $class
+     * @param \ReflectionMethod $method
+     *
+     * @return string
+     */
+    protected function getDefaultRouteName(\ReflectionClass $class, \ReflectionMethod $method)
+    {
+        $name = strtolower(str_replace('\\', '_', $class->name).'_'.$method->name);
+        if ($this->defaultRouteIndex > 0) {
+            $name .= '_'.$this->defaultRouteIndex;
+        }
+        $this->defaultRouteIndex++;
+
+        return $name;
+    }
+
+    abstract protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, $annot);
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Loader/AnnotationDirectoryLoader.php b/vendor/symfony/routing/Symfony/Component/Routing/Loader/AnnotationDirectoryLoader.php
new file mode 100644 (file)
index 0000000..abd68ed
--- /dev/null
@@ -0,0 +1,77 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Loader;
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Config\Resource\DirectoryResource;
+
+/**
+ * AnnotationDirectoryLoader loads routing information from annotations set
+ * on PHP classes and methods.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class AnnotationDirectoryLoader extends AnnotationFileLoader
+{
+    /**
+     * Loads from annotations from a directory.
+     *
+     * @param string      $path A directory path
+     * @param string|null $type The resource type
+     *
+     * @return RouteCollection A RouteCollection instance
+     *
+     * @throws \InvalidArgumentException When the directory does not exist or its routes cannot be parsed
+     */
+    public function load($path, $type = null)
+    {
+        $dir = $this->locator->locate($path);
+
+        $collection = new RouteCollection();
+        $collection->addResource(new DirectoryResource($dir, '/\.php$/'));
+        $files = iterator_to_array(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir), \RecursiveIteratorIterator::LEAVES_ONLY));
+        usort($files, function (\SplFileInfo $a, \SplFileInfo $b) {
+            return (string) $a > (string) $b ? 1 : -1;
+        });
+
+        foreach ($files as $file) {
+            if (!$file->isFile() || '.php' !== substr($file->getFilename(), -4)) {
+                continue;
+            }
+
+            if ($class = $this->findClass($file)) {
+                $refl = new \ReflectionClass($class);
+                if ($refl->isAbstract()) {
+                    continue;
+                }
+
+                $collection->addCollection($this->loader->load($class, $type));
+            }
+        }
+
+        return $collection;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function supports($resource, $type = null)
+    {
+        try {
+            $path = $this->locator->locate($resource);
+        } catch (\Exception $e) {
+            return false;
+        }
+
+        return is_string($resource) && is_dir($path) && (!$type || 'annotation' === $type);
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Loader/AnnotationFileLoader.php b/vendor/symfony/routing/Symfony/Component/Routing/Loader/AnnotationFileLoader.php
new file mode 100644 (file)
index 0000000..33776fd
--- /dev/null
@@ -0,0 +1,122 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Loader;
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Config\Loader\FileLoader;
+use Symfony\Component\Config\FileLocatorInterface;
+
+/**
+ * AnnotationFileLoader loads routing information from annotations set
+ * on a PHP class and its methods.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class AnnotationFileLoader extends FileLoader
+{
+    protected $loader;
+
+    /**
+     * Constructor.
+     *
+     * @param FileLocatorInterface  $locator A FileLocator instance
+     * @param AnnotationClassLoader $loader  An AnnotationClassLoader instance
+     * @param string|array          $paths   A path or an array of paths where to look for resources
+     *
+     * @throws \RuntimeException
+     */
+    public function __construct(FileLocatorInterface $locator, AnnotationClassLoader $loader, $paths = array())
+    {
+        if (!function_exists('token_get_all')) {
+            throw new \RuntimeException('The Tokenizer extension is required for the routing annotation loaders.');
+        }
+
+        parent::__construct($locator, $paths);
+
+        $this->loader = $loader;
+    }
+
+    /**
+     * Loads from annotations from a file.
+     *
+     * @param string      $file A PHP file path
+     * @param string|null $type The resource type
+     *
+     * @return RouteCollection A RouteCollection instance
+     *
+     * @throws \InvalidArgumentException When the file does not exist or its routes cannot be parsed
+     */
+    public function load($file, $type = null)
+    {
+        $path = $this->locator->locate($file);
+
+        $collection = new RouteCollection();
+        if ($class = $this->findClass($path)) {
+            $collection->addResource(new FileResource($path));
+            $collection->addCollection($this->loader->load($class, $type));
+        }
+
+        return $collection;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function supports($resource, $type = null)
+    {
+        return is_string($resource) && 'php' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'annotation' === $type);
+    }
+
+    /**
+     * Returns the full class name for the first class in the file.
+     *
+     * @param string $file A PHP file path
+     *
+     * @return string|false Full class name if found, false otherwise
+     */
+    protected function findClass($file)
+    {
+        $class = false;
+        $namespace = false;
+        $tokens = token_get_all(file_get_contents($file));
+        for ($i = 0, $count = count($tokens); $i < $count; $i++) {
+            $token = $tokens[$i];
+
+            if (!is_array($token)) {
+                continue;
+            }
+
+            if (true === $class && T_STRING === $token[0]) {
+                return $namespace.'\\'.$token[1];
+            }
+
+            if (true === $namespace && T_STRING === $token[0]) {
+                $namespace = '';
+                do {
+                    $namespace .= $token[1];
+                    $token = $tokens[++$i];
+                } while ($i < $count && is_array($token) && in_array($token[0], array(T_NS_SEPARATOR, T_STRING)));
+            }
+
+            if (T_CLASS === $token[0]) {
+                $class = true;
+            }
+
+            if (T_NAMESPACE === $token[0]) {
+                $namespace = true;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Loader/ClosureLoader.php b/vendor/symfony/routing/Symfony/Component/Routing/Loader/ClosureLoader.php
new file mode 100644 (file)
index 0000000..8212c29
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Loader;
+
+use Symfony\Component\Config\Loader\Loader;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * ClosureLoader loads routes from a PHP closure.
+ *
+ * The Closure must return a RouteCollection instance.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class ClosureLoader extends Loader
+{
+    /**
+     * Loads a Closure.
+     *
+     * @param \Closure    $closure A Closure
+     * @param string|null $type    The resource type
+     *
+     * @return RouteCollection A RouteCollection instance
+     *
+     * @api
+     */
+    public function load($closure, $type = null)
+    {
+        return call_user_func($closure);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function supports($resource, $type = null)
+    {
+        return $resource instanceof \Closure && (!$type || 'closure' === $type);
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Loader/PhpFileLoader.php b/vendor/symfony/routing/Symfony/Component/Routing/Loader/PhpFileLoader.php
new file mode 100644 (file)
index 0000000..98b7efb
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Loader;
+
+use Symfony\Component\Config\Loader\FileLoader;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * PhpFileLoader loads routes from a PHP file.
+ *
+ * The file must return a RouteCollection instance.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class PhpFileLoader extends FileLoader
+{
+    /**
+     * Loads a PHP file.
+     *
+     * @param string      $file A PHP file path
+     * @param string|null $type The resource type
+     *
+     * @return RouteCollection A RouteCollection instance
+     *
+     * @api
+     */
+    public function load($file, $type = null)
+    {
+        // the loader variable is exposed to the included file below
+        $loader = $this;
+
+        $path = $this->locator->locate($file);
+        $this->setCurrentDir(dirname($path));
+
+        $collection = include $path;
+        $collection->addResource(new FileResource($path));
+
+        return $collection;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function supports($resource, $type = null)
+    {
+        return is_string($resource) && 'php' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'php' === $type);
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Loader/XmlFileLoader.php b/vendor/symfony/routing/Symfony/Component/Routing/Loader/XmlFileLoader.php
new file mode 100644 (file)
index 0000000..da7b33d
--- /dev/null
@@ -0,0 +1,238 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Loader;
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Config\Loader\FileLoader;
+use Symfony\Component\Config\Util\XmlUtils;
+
+/**
+ * XmlFileLoader loads XML routing files.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Tobias Schultze <http://tobion.de>
+ *
+ * @api
+ */
+class XmlFileLoader extends FileLoader
+{
+    const NAMESPACE_URI = 'http://symfony.com/schema/routing';
+    const SCHEME_PATH = '/schema/routing/routing-1.0.xsd';
+
+    /**
+     * Loads an XML file.
+     *
+     * @param string      $file An XML file path
+     * @param string|null $type The resource type
+     *
+     * @return RouteCollection A RouteCollection instance
+     *
+     * @throws \InvalidArgumentException When the file cannot be loaded or when the XML cannot be
+     *                                   parsed because it does not validate against the scheme.
+     *
+     * @api
+     */
+    public function load($file, $type = null)
+    {
+        $path = $this->locator->locate($file);
+
+        $xml = $this->loadFile($path);
+
+        $collection = new RouteCollection();
+        $collection->addResource(new FileResource($path));
+
+        // process routes and imports
+        foreach ($xml->documentElement->childNodes as $node) {
+            if (!$node instanceof \DOMElement) {
+                continue;
+            }
+
+            $this->parseNode($collection, $node, $path, $file);
+        }
+
+        return $collection;
+    }
+
+    /**
+     * Parses a node from a loaded XML file.
+     *
+     * @param RouteCollection $collection Collection to associate with the node
+     * @param \DOMElement     $node       Element to parse
+     * @param string          $path       Full path of the XML file being processed
+     * @param string          $file       Loaded file name
+     *
+     * @throws \InvalidArgumentException When the XML is invalid
+     */
+    protected function parseNode(RouteCollection $collection, \DOMElement $node, $path, $file)
+    {
+        if (self::NAMESPACE_URI !== $node->namespaceURI) {
+            return;
+        }
+
+        switch ($node->localName) {
+            case 'route':
+                $this->parseRoute($collection, $node, $path);
+                break;
+            case 'import':
+                $this->parseImport($collection, $node, $path, $file);
+                break;
+            default:
+                throw new \InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "route" or "import".', $node->localName, $path));
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function supports($resource, $type = null)
+    {
+        return is_string($resource) && 'xml' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'xml' === $type);
+    }
+
+    /**
+     * Parses a route and adds it to the RouteCollection.
+     *
+     * @param RouteCollection $collection RouteCollection instance
+     * @param \DOMElement     $node       Element to parse that represents a Route
+     * @param string          $path       Full path of the XML file being processed
+     *
+     * @throws \InvalidArgumentException When the XML is invalid
+     */
+    protected function parseRoute(RouteCollection $collection, \DOMElement $node, $path)
+    {
+        if ('' === ($id = $node->getAttribute('id')) || (!$node->hasAttribute('pattern') && !$node->hasAttribute('path'))) {
+            throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must have an "id" and a "path" attribute.', $path));
+        }
+
+        if ($node->hasAttribute('pattern')) {
+            if ($node->hasAttribute('path')) {
+                throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" cannot define both a "path" and a "pattern" attribute. Use only "path".', $path));
+            }
+
+            $node->setAttribute('path', $node->getAttribute('pattern'));
+            $node->removeAttribute('pattern');
+        }
+
+        $schemes = preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, PREG_SPLIT_NO_EMPTY);
+        $methods = preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, PREG_SPLIT_NO_EMPTY);
+
+        list($defaults, $requirements, $options) = $this->parseConfigs($node, $path);
+
+        $route = new Route($node->getAttribute('path'), $defaults, $requirements, $options, $node->getAttribute('host'), $schemes, $methods);
+        $collection->add($id, $route);
+    }
+
+    /**
+     * Parses an import and adds the routes in the resource to the RouteCollection.
+     *
+     * @param RouteCollection $collection RouteCollection instance
+     * @param \DOMElement     $node       Element to parse that represents a Route
+     * @param string          $path       Full path of the XML file being processed
+     * @param string          $file       Loaded file name
+     *
+     * @throws \InvalidArgumentException When the XML is invalid
+     */
+    protected function parseImport(RouteCollection $collection, \DOMElement $node, $path, $file)
+    {
+        if ('' === $resource = $node->getAttribute('resource')) {
+            throw new \InvalidArgumentException(sprintf('The <import> element in file "%s" must have a "resource" attribute.', $path));
+        }
+
+        $type = $node->getAttribute('type');
+        $prefix = $node->getAttribute('prefix');
+        $host = $node->hasAttribute('host') ? $node->getAttribute('host') : null;
+        $schemes = $node->hasAttribute('schemes') ? preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, PREG_SPLIT_NO_EMPTY) : null;
+        $methods = $node->hasAttribute('methods') ? preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, PREG_SPLIT_NO_EMPTY) : null;
+
+        list($defaults, $requirements, $options) = $this->parseConfigs($node, $path);
+
+        $this->setCurrentDir(dirname($path));
+
+        $subCollection = $this->import($resource, ('' !== $type ? $type : null), false, $file);
+        /* @var $subCollection RouteCollection */
+        $subCollection->addPrefix($prefix);
+        if (null !== $host) {
+            $subCollection->setHost($host);
+        }
+        if (null !== $schemes) {
+            $subCollection->setSchemes($schemes);
+        }
+        if (null !== $methods) {
+            $subCollection->setMethods($methods);
+        }
+        $subCollection->addDefaults($defaults);
+        $subCollection->addRequirements($requirements);
+        $subCollection->addOptions($options);
+
+        $collection->addCollection($subCollection);
+    }
+
+    /**
+     * Loads an XML file.
+     *
+     * @param string $file An XML file path
+     *
+     * @return \DOMDocument
+     *
+     * @throws \InvalidArgumentException When loading of XML file fails because of syntax errors
+     *                                   or when the XML structure is not as expected by the scheme -
+     *                                   see validate()
+     */
+    protected function loadFile($file)
+    {
+        return XmlUtils::loadFile($file, __DIR__.static::SCHEME_PATH);
+    }
+
+    /**
+     * Parses the config elements (default, requirement, option).
+     *
+     * @param \DOMElement $node Element to parse that contains the configs
+     * @param string      $path Full path of the XML file being processed
+     *
+     * @return array An array with the defaults as first item, requirements as second and options as third.
+     *
+     * @throws \InvalidArgumentException When the XML is invalid
+     */
+    private function parseConfigs(\DOMElement $node, $path)
+    {
+        $defaults = array();
+        $requirements = array();
+        $options = array();
+
+        foreach ($node->getElementsByTagNameNS(self::NAMESPACE_URI, '*') as $n) {
+            switch ($n->localName) {
+                case 'default':
+                    if ($n->hasAttribute('xsi:nil') && 'true' == $n->getAttribute('xsi:nil')) {
+                        $defaults[$n->getAttribute('key')] = null;
+                    } else {
+                        $defaults[$n->getAttribute('key')] = trim($n->textContent);
+                    }
+
+                    break;
+                case 'requirement':
+                    $requirements[$n->getAttribute('key')] = trim($n->textContent);
+                    break;
+                case 'option':
+                    $options[$n->getAttribute('key')] = trim($n->textContent);
+                    break;
+                default:
+                    throw new \InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "default", "requirement" or "option".', $n->localName, $path));
+            }
+        }
+
+        return array($defaults, $requirements, $options);
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Loader/YamlFileLoader.php b/vendor/symfony/routing/Symfony/Component/Routing/Loader/YamlFileLoader.php
new file mode 100644 (file)
index 0000000..9deea7f
--- /dev/null
@@ -0,0 +1,212 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Loader;
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Yaml\Parser as YamlParser;
+use Symfony\Component\Config\Loader\FileLoader;
+
+/**
+ * YamlFileLoader loads Yaml routing files.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Tobias Schultze <http://tobion.de>
+ *
+ * @api
+ */
+class YamlFileLoader extends FileLoader
+{
+    private static $availableKeys = array(
+        'resource', 'type', 'prefix', 'pattern', 'path', 'host', 'schemes', 'methods', 'defaults', 'requirements', 'options',
+    );
+    private $yamlParser;
+
+    /**
+     * Loads a Yaml file.
+     *
+     * @param string      $file A Yaml file path
+     * @param string|null $type The resource type
+     *
+     * @return RouteCollection A RouteCollection instance
+     *
+     * @throws \InvalidArgumentException When a route can't be parsed because YAML is invalid
+     *
+     * @api
+     */
+    public function load($file, $type = null)
+    {
+        $path = $this->locator->locate($file);
+
+        if (!stream_is_local($path)) {
+            throw new \InvalidArgumentException(sprintf('This is not a local file "%s".', $path));
+        }
+
+        if (!file_exists($path)) {
+            throw new \InvalidArgumentException(sprintf('File "%s" not found.', $path));
+        }
+
+        if (null === $this->yamlParser) {
+            $this->yamlParser = new YamlParser();
+        }
+
+        $config = $this->yamlParser->parse(file_get_contents($path));
+
+        $collection = new RouteCollection();
+        $collection->addResource(new FileResource($path));
+
+        // empty file
+        if (null === $config) {
+            return $collection;
+        }
+
+        // not an array
+        if (!is_array($config)) {
+            throw new \InvalidArgumentException(sprintf('The file "%s" must contain a YAML array.', $path));
+        }
+
+        foreach ($config as $name => $config) {
+            if (isset($config['pattern'])) {
+                if (isset($config['path'])) {
+                    throw new \InvalidArgumentException(sprintf('The file "%s" cannot define both a "path" and a "pattern" attribute. Use only "path".', $path));
+                }
+
+                $config['path'] = $config['pattern'];
+                unset($config['pattern']);
+            }
+
+            $this->validate($config, $name, $path);
+
+            if (isset($config['resource'])) {
+                $this->parseImport($collection, $config, $path, $file);
+            } else {
+                $this->parseRoute($collection, $name, $config, $path);
+            }
+        }
+
+        return $collection;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function supports($resource, $type = null)
+    {
+        return is_string($resource) && 'yml' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'yaml' === $type);
+    }
+
+    /**
+     * Parses a route and adds it to the RouteCollection.
+     *
+     * @param RouteCollection $collection A RouteCollection instance
+     * @param string          $name       Route name
+     * @param array           $config     Route definition
+     * @param string          $path       Full path of the YAML file being processed
+     */
+    protected function parseRoute(RouteCollection $collection, $name, array $config, $path)
+    {
+        $defaults = isset($config['defaults']) ? $config['defaults'] : array();
+        $requirements = isset($config['requirements']) ? $config['requirements'] : array();
+        $options = isset($config['options']) ? $config['options'] : array();
+        $host = isset($config['host']) ? $config['host'] : '';
+        $schemes = isset($config['schemes']) ? $config['schemes'] : array();
+        $methods = isset($config['methods']) ? $config['methods'] : array();
+
+        $route = new Route($config['path'], $defaults, $requirements, $options, $host, $schemes, $methods);
+
+        $collection->add($name, $route);
+    }
+
+    /**
+     * Parses an import and adds the routes in the resource to the RouteCollection.
+     *
+     * @param RouteCollection $collection A RouteCollection instance
+     * @param array           $config     Route definition
+     * @param string          $path       Full path of the YAML file being processed
+     * @param string          $file       Loaded file name
+     */
+    protected function parseImport(RouteCollection $collection, array $config, $path, $file)
+    {
+        $type = isset($config['type']) ? $config['type'] : null;
+        $prefix = isset($config['prefix']) ? $config['prefix'] : '';
+        $defaults = isset($config['defaults']) ? $config['defaults'] : array();
+        $requirements = isset($config['requirements']) ? $config['requirements'] : array();
+        $options = isset($config['options']) ? $config['options'] : array();
+        $host = isset($config['host']) ? $config['host'] : null;
+        $schemes = isset($config['schemes']) ? $config['schemes'] : null;
+        $methods = isset($config['methods']) ? $config['methods'] : null;
+
+        $this->setCurrentDir(dirname($path));
+
+        $subCollection = $this->import($config['resource'], $type, false, $file);
+        /* @var $subCollection RouteCollection */
+        $subCollection->addPrefix($prefix);
+        if (null !== $host) {
+            $subCollection->setHost($host);
+        }
+        if (null !== $schemes) {
+            $subCollection->setSchemes($schemes);
+        }
+        if (null !== $methods) {
+            $subCollection->setMethods($methods);
+        }
+        $subCollection->addDefaults($defaults);
+        $subCollection->addRequirements($requirements);
+        $subCollection->addOptions($options);
+
+        $collection->addCollection($subCollection);
+    }
+
+    /**
+     * Validates the route configuration.
+     *
+     * @param array  $config A resource config
+     * @param string $name   The config key
+     * @param string $path   The loaded file path
+     *
+     * @throws \InvalidArgumentException If one of the provided config keys is not supported,
+     *                                   something is missing or the combination is nonsense
+     */
+    protected function validate($config, $name, $path)
+    {
+        if (!is_array($config)) {
+            throw new \InvalidArgumentException(sprintf('The definition of "%s" in "%s" must be a YAML array.', $name, $path));
+        }
+        if ($extraKeys = array_diff(array_keys($config), self::$availableKeys)) {
+            throw new \InvalidArgumentException(sprintf(
+                'The routing file "%s" contains unsupported keys for "%s": "%s". Expected one of: "%s".',
+                $path, $name, implode('", "', $extraKeys), implode('", "', self::$availableKeys)
+            ));
+        }
+        if (isset($config['resource']) && isset($config['path'])) {
+            throw new \InvalidArgumentException(sprintf(
+                'The routing file "%s" must not specify both the "resource" key and the "path" key for "%s". Choose between an import and a route definition.',
+                $path, $name
+            ));
+        }
+        if (!isset($config['resource']) && isset($config['type'])) {
+            throw new \InvalidArgumentException(sprintf(
+                'The "type" key for the route definition "%s" in "%s" is unsupported. It is only available for imports in combination with the "resource" key.',
+                $name, $path
+            ));
+        }
+        if (!isset($config['resource']) && !isset($config['path'])) {
+            throw new \InvalidArgumentException(sprintf(
+                'You must define a "path" for the route "%s" in file "%s".',
+                $name, $path
+            ));
+        }
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd b/vendor/symfony/routing/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd
new file mode 100644 (file)
index 0000000..daea814
--- /dev/null
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<xsd:schema xmlns="http://symfony.com/schema/routing"
+    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+    targetNamespace="http://symfony.com/schema/routing"
+    elementFormDefault="qualified">
+
+  <xsd:annotation>
+    <xsd:documentation><![CDATA[
+      Symfony XML Routing Schema, version 1.0
+      Authors: Fabien Potencier, Tobias Schultze
+
+      This scheme defines the elements and attributes that can be used to define
+      routes. A route maps an HTTP request to a set of configuration variables.
+    ]]></xsd:documentation>
+  </xsd:annotation>
+
+  <xsd:element name="routes" type="routes" />
+
+  <xsd:complexType name="routes">
+    <xsd:choice minOccurs="0" maxOccurs="unbounded">
+      <xsd:element name="import" type="import" />
+      <xsd:element name="route" type="route" />
+    </xsd:choice>
+  </xsd:complexType>
+
+  <xsd:group name="configs">
+    <xsd:choice>
+      <xsd:element name="default" nillable="true" type="element" />
+      <xsd:element name="requirement" type="element" />
+      <xsd:element name="option" type="element" />
+    </xsd:choice>
+  </xsd:group>
+
+  <xsd:complexType name="route">
+    <xsd:group ref="configs" minOccurs="0" maxOccurs="unbounded" />
+
+    <xsd:attribute name="id" type="xsd:string" use="required" />
+    <xsd:attribute name="path" type="xsd:string" />
+    <xsd:attribute name="pattern" type="xsd:string" />
+    <xsd:attribute name="host" type="xsd:string" />
+    <xsd:attribute name="schemes" type="xsd:string" />
+    <xsd:attribute name="methods" type="xsd:string" />
+  </xsd:complexType>
+
+  <xsd:complexType name="import">
+    <xsd:group ref="configs" minOccurs="0" maxOccurs="unbounded" />
+
+    <xsd:attribute name="resource" type="xsd:string" use="required" />
+    <xsd:attribute name="type" type="xsd:string" />
+    <xsd:attribute name="prefix" type="xsd:string" />
+    <xsd:attribute name="host" type="xsd:string" />
+    <xsd:attribute name="schemes" type="xsd:string" />
+    <xsd:attribute name="methods" type="xsd:string" />
+  </xsd:complexType>
+
+  <xsd:complexType name="element">
+    <xsd:simpleContent>
+      <xsd:extension base="xsd:string">
+        <xsd:attribute name="key" type="xsd:string" use="required" />
+      </xsd:extension>
+    </xsd:simpleContent>
+  </xsd:complexType>
+</xsd:schema>
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Matcher/ApacheUrlMatcher.php b/vendor/symfony/routing/Symfony/Component/Routing/Matcher/ApacheUrlMatcher.php
new file mode 100644 (file)
index 0000000..76612e6
--- /dev/null
@@ -0,0 +1,94 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Matcher;
+
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+
+/**
+ * ApacheUrlMatcher matches URL based on Apache mod_rewrite matching (see ApacheMatcherDumper).
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Arnaud Le Blanc <arnaud.lb@gmail.com>
+ */
+class ApacheUrlMatcher extends UrlMatcher
+{
+    /**
+     * Tries to match a URL based on Apache mod_rewrite matching.
+     *
+     * Returns false if no route matches the URL.
+     *
+     * @param string $pathinfo The pathinfo to be parsed
+     *
+     * @return array An array of parameters
+     *
+     * @throws MethodNotAllowedException If the current method is not allowed
+     */
+    public function match($pathinfo)
+    {
+        $parameters = array();
+        $defaults = array();
+        $allow = array();
+        $route = null;
+
+        foreach ($_SERVER as $key => $value) {
+            $name = $key;
+
+            // skip non-routing variables
+            // this improves performance when $_SERVER contains many usual
+            // variables like HTTP_*, DOCUMENT_ROOT, REQUEST_URI, ...
+            if (false === strpos($name, '_ROUTING_')) {
+                continue;
+            }
+
+            while (0 === strpos($name, 'REDIRECT_')) {
+                $name = substr($name, 9);
+            }
+
+            // expect _ROUTING_<type>_<name>
+            // or _ROUTING_<type>
+
+            if (0 !== strpos($name, '_ROUTING_')) {
+                continue;
+            }
+            if (false !== $pos = strpos($name, '_', 9)) {
+                $type = substr($name, 9, $pos-9);
+                $name = substr($name, $pos+1);
+            } else {
+                $type = substr($name, 9);
+            }
+
+            if ('param' === $type) {
+                if ('' !== $value) {
+                    $parameters[$name] = $value;
+                }
+            } elseif ('default' === $type) {
+                $defaults[$name] = $value;
+            } elseif ('route' === $type) {
+                $route = $value;
+            } elseif ('allow' === $type) {
+                $allow[] = $name;
+            }
+
+            unset($_SERVER[$key]);
+        }
+
+        if (null !== $route) {
+            $parameters['_route'] = $route;
+
+            return $this->mergeDefaults($parameters, $defaults);
+        } elseif (0 < count($allow)) {
+            throw new MethodNotAllowedException($allow);
+        } else {
+            return parent::match($pathinfo);
+        }
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/ApacheMatcherDumper.php b/vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/ApacheMatcherDumper.php
new file mode 100644 (file)
index 0000000..804da19
--- /dev/null
@@ -0,0 +1,252 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Matcher\Dumper;
+
+use Symfony\Component\Routing\Route;
+
+/**
+ * Dumps a set of Apache mod_rewrite rules.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Kris Wallsmith <kris@symfony.com>
+ */
+class ApacheMatcherDumper extends MatcherDumper
+{
+    /**
+     * Dumps a set of Apache mod_rewrite rules.
+     *
+     * Available options:
+     *
+     *  * script_name: The script name (app.php by default)
+     *  * base_uri:    The base URI ("" by default)
+     *
+     * @param array $options An array of options
+     *
+     * @return string A string to be used as Apache rewrite rules
+     *
+     * @throws \LogicException When the route regex is invalid
+     */
+    public function dump(array $options = array())
+    {
+        $options = array_merge(array(
+            'script_name' => 'app.php',
+            'base_uri'    => '',
+        ), $options);
+
+        $options['script_name'] = self::escape($options['script_name'], ' ', '\\');
+
+        $rules = array("# skip \"real\" requests\nRewriteCond %{REQUEST_FILENAME} -f\nRewriteRule .* - [QSA,L]");
+        $methodVars = array();
+        $hostRegexUnique = 0;
+        $prevHostRegex = '';
+
+        foreach ($this->getRoutes()->all() as $name => $route) {
+
+            $compiledRoute = $route->compile();
+            $hostRegex = $compiledRoute->getHostRegex();
+
+            if (null !== $hostRegex && $prevHostRegex !== $hostRegex) {
+                $prevHostRegex = $hostRegex;
+                $hostRegexUnique++;
+
+                $rule = array();
+
+                $regex = $this->regexToApacheRegex($hostRegex);
+                $regex = self::escape($regex, ' ', '\\');
+
+                $rule[] = sprintf('RewriteCond %%{HTTP:Host} %s', $regex);
+
+                $variables = array();
+                $variables[] = sprintf('E=__ROUTING_host_%s:1', $hostRegexUnique);
+
+                foreach ($compiledRoute->getHostVariables() as $i => $variable) {
+                    $variables[] = sprintf('E=__ROUTING_host_%s_%s:%%%d', $hostRegexUnique, $variable, $i+1);
+                }
+
+                $variables = implode(',', $variables);
+
+                $rule[] = sprintf('RewriteRule .? - [%s]', $variables);
+
+                $rules[] = implode("\n", $rule);
+            }
+
+            $rules[] = $this->dumpRoute($name, $route, $options, $hostRegexUnique);
+
+            if ($req = $route->getRequirement('_method')) {
+                $methods = explode('|', strtoupper($req));
+                $methodVars = array_merge($methodVars, $methods);
+            }
+        }
+        if (0 < count($methodVars)) {
+            $rule = array('# 405 Method Not Allowed');
+            $methodVars = array_values(array_unique($methodVars));
+            if (in_array('GET', $methodVars) && !in_array('HEAD', $methodVars)) {
+                $methodVars[] = 'HEAD';
+            }
+            foreach ($methodVars as $i => $methodVar) {
+                $rule[] = sprintf('RewriteCond %%{ENV:_ROUTING__allow_%s} =1%s', $methodVar, isset($methodVars[$i + 1]) ? ' [OR]' : '');
+            }
+            $rule[] = sprintf('RewriteRule .* %s [QSA,L]', $options['script_name']);
+
+            $rules[] = implode("\n", $rule);
+        }
+
+        return implode("\n\n", $rules)."\n";
+    }
+
+    /**
+     * Dumps a single route
+     *
+     * @param  string $name Route name
+     * @param  Route  $route The route
+     * @param  array  $options Options
+     * @param  bool   $hostRegexUnique Unique identifier for the host regex
+     *
+     * @return string The compiled route
+     */
+    private function dumpRoute($name, $route, array $options, $hostRegexUnique)
+    {
+        $compiledRoute = $route->compile();
+
+        // prepare the apache regex
+        $regex = $this->regexToApacheRegex($compiledRoute->getRegex());
+        $regex = '^'.self::escape(preg_quote($options['base_uri']).substr($regex, 1), ' ', '\\');
+
+        $methods = $this->getRouteMethods($route);
+
+        $hasTrailingSlash = (!$methods || in_array('HEAD', $methods)) && '/$' === substr($regex, -2) && '^/$' !== $regex;
+
+        $variables = array('E=_ROUTING_route:'.$name);
+        foreach ($compiledRoute->getHostVariables() as $variable) {
+            $variables[] = sprintf('E=_ROUTING_param_%s:%%{ENV:__ROUTING_host_%s_%s}', $variable, $hostRegexUnique, $variable);
+        }
+        foreach ($compiledRoute->getPathVariables() as $i => $variable) {
+            $variables[] = 'E=_ROUTING_param_'.$variable.':%'.($i + 1);
+        }
+        foreach ($route->getDefaults() as $key => $value) {
+            $variables[] = 'E=_ROUTING_default_'.$key.':'.strtr($value, array(
+                ':'  => '\\:',
+                '='  => '\\=',
+                '\\' => '\\\\',
+                ' '  => '\\ ',
+            ));
+        }
+        $variables = implode(',', $variables);
+
+        $rule = array("# $name");
+
+        // method mismatch
+        if (0 < count($methods)) {
+            $allow = array();
+            foreach ($methods as $method) {
+                $allow[] = 'E=_ROUTING_allow_'.$method.':1';
+            }
+
+            if ($hostRegex = $compiledRoute->getHostRegex()) {
+                $rule[] = sprintf("RewriteCond %%{ENV:__ROUTING_host_%s} =1", $hostRegexUnique);
+            }
+
+            $rule[] = "RewriteCond %{REQUEST_URI} $regex";
+            $rule[] = sprintf("RewriteCond %%{REQUEST_METHOD} !^(%s)$ [NC]", implode('|', $methods));
+            $rule[] = sprintf('RewriteRule .* - [S=%d,%s]', $hasTrailingSlash ? 2 : 1, implode(',', $allow));
+        }
+
+        // redirect with trailing slash appended
+        if ($hasTrailingSlash) {
+
+            if ($hostRegex = $compiledRoute->getHostRegex()) {
+                $rule[] = sprintf("RewriteCond %%{ENV:__ROUTING_host_%s} =1", $hostRegexUnique);
+            }
+
+            $rule[] = 'RewriteCond %{REQUEST_URI} '.substr($regex, 0, -2).'$';
+            $rule[] = 'RewriteRule .* $0/ [QSA,L,R=301]';
+        }
+
+        // the main rule
+
+        if ($hostRegex = $compiledRoute->getHostRegex()) {
+            $rule[] = sprintf("RewriteCond %%{ENV:__ROUTING_host_%s} =1", $hostRegexUnique);
+        }
+
+        $rule[] = "RewriteCond %{REQUEST_URI} $regex";
+        $rule[] = "RewriteRule .* {$options['script_name']} [QSA,L,$variables]";
+
+        return implode("\n", $rule);
+    }
+
+    /**
+     * Returns methods allowed for a route
+     *
+     * @param Route  $route The route
+     *
+     * @return array The methods
+     */
+    private function getRouteMethods(Route $route)
+    {
+        $methods = array();
+        if ($req = $route->getRequirement('_method')) {
+            $methods = explode('|', strtoupper($req));
+            // GET and HEAD are equivalent
+            if (in_array('GET', $methods) && !in_array('HEAD', $methods)) {
+                $methods[] = 'HEAD';
+            }
+        }
+
+        return $methods;
+    }
+
+    /**
+     * Converts a regex to make it suitable for mod_rewrite
+     *
+     * @param string  $regex The regex
+     *
+     * @return string The converted regex
+     */
+    private function regexToApacheRegex($regex)
+    {
+        $regexPatternEnd = strrpos($regex, $regex[0]);
+
+        return preg_replace('/\?P<.+?>/', '', substr($regex, 1, $regexPatternEnd - 1));
+    }
+
+    /**
+     * Escapes a string.
+     *
+     * @param string $string The string to be escaped
+     * @param string $char   The character to be escaped
+     * @param string $with   The character to be used for escaping
+     *
+     * @return string The escaped string
+     */
+    private static function escape($string, $char, $with)
+    {
+        $escaped = false;
+        $output = '';
+        foreach (str_split($string) as $symbol) {
+            if ($escaped) {
+                $output .= $symbol;
+                $escaped = false;
+                continue;
+            }
+            if ($symbol === $char) {
+                $output .= $with.$char;
+                continue;
+            }
+            if ($symbol === $with) {
+                $escaped = true;
+            }
+            $output .= $symbol;
+        }
+
+        return $output;
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/DumperCollection.php b/vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/DumperCollection.php
new file mode 100644 (file)
index 0000000..612ac0d
--- /dev/null
@@ -0,0 +1,159 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Matcher\Dumper;
+
+/**
+ * Collection of routes.
+ *
+ * @author Arnaud Le Blanc <arnaud.lb@gmail.com>
+ */
+class DumperCollection implements \IteratorAggregate
+{
+    /**
+     * @var DumperCollection|null
+     */
+    private $parent;
+
+    /**
+     * @var (DumperCollection|DumperRoute)[]
+     */
+    private $children = array();
+
+    /**
+     * @var array
+     */
+    private $attributes = array();
+
+    /**
+     * Returns the children routes and collections.
+     *
+     * @return (DumperCollection|DumperRoute)[] Array of DumperCollection|DumperRoute
+     */
+    public function all()
+    {
+        return $this->children;
+    }
+
+    /**
+     * Adds a route or collection
+     *
+     * @param DumperRoute|DumperCollection The route or collection
+     */
+    public function add($child)
+    {
+        if ($child instanceof DumperCollection) {
+            $child->setParent($this);
+        }
+        $this->children[] = $child;
+    }
+
+    /**
+     * Sets children.
+     *
+     * @param array $children The children
+     */
+    public function setAll(array $children)
+    {
+        foreach ($children as $child) {
+            if ($child instanceof DumperCollection) {
+                $child->setParent($this);
+            }
+        }
+        $this->children = $children;
+    }
+
+    /**
+     * Returns an iterator over the children.
+     *
+     * @return \Iterator The iterator
+     */
+    public function getIterator()
+    {
+        return new \ArrayIterator($this->children);
+    }
+
+    /**
+     * Returns the root of the collection.
+     *
+     * @return DumperCollection The root collection
+     */
+    public function getRoot()
+    {
+        return (null !== $this->parent) ? $this->parent->getRoot() : $this;
+    }
+
+    /**
+     * Returns the parent collection.
+     *
+     * @return DumperCollection|null The parent collection or null if the collection has no parent
+     */
+    protected function getParent()
+    {
+        return $this->parent;
+    }
+
+    /**
+     * Sets the parent collection.
+     *
+     * @param DumperCollection $parent The parent collection
+     */
+    protected function setParent(DumperCollection $parent)
+    {
+        $this->parent = $parent;
+    }
+
+    /**
+     * Returns true if the attribute is defined.
+     *
+     * @param string $name The attribute name
+     *
+     * @return Boolean true if the attribute is defined, false otherwise
+     */
+    public function hasAttribute($name)
+    {
+        return array_key_exists($name, $this->attributes);
+    }
+
+    /**
+     * Returns an attribute by name.
+     *
+     * @param string $name    The attribute name
+     * @param mixed  $default Default value is the attribute doesn't exist
+     *
+     * @return mixed The attribute value
+     */
+    public function getAttribute($name, $default = null)
+    {
+        return $this->hasAttribute($name) ? $this->attributes[$name] : $default;
+    }
+
+    /**
+     * Sets an attribute by name.
+     *
+     * @param string $name  The attribute name
+     * @param mixed  $value The attribute value
+     */
+    public function setAttribute($name, $value)
+    {
+        $this->attributes[$name] = $value;
+    }
+
+    /**
+     * Sets multiple attributes.
+     *
+     * @param array $attributes The attributes
+     */
+    public function setAttributes($attributes)
+    {
+        $this->attributes = $attributes;
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/DumperPrefixCollection.php b/vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/DumperPrefixCollection.php
new file mode 100644 (file)
index 0000000..26382b0
--- /dev/null
@@ -0,0 +1,108 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Matcher\Dumper;
+
+/**
+ * Prefix tree of routes preserving routes order.
+ *
+ * @author Arnaud Le Blanc <arnaud.lb@gmail.com>
+ */
+class DumperPrefixCollection extends DumperCollection
+{
+    /**
+     * @var string
+     */
+    private $prefix = '';
+
+    /**
+     * Returns the prefix.
+     *
+     * @return string The prefix
+     */
+    public function getPrefix()
+    {
+        return $this->prefix;
+    }
+
+    /**
+     * Sets the prefix.
+     *
+     * @param string $prefix The prefix
+     */
+    public function setPrefix($prefix)
+    {
+        $this->prefix = $prefix;
+    }
+
+    /**
+     * Adds a route in the tree.
+     *
+     * @param DumperRoute $route The route
+     *
+     * @return DumperPrefixCollection The node the route was added to
+     *
+     * @throws \LogicException
+     */
+    public function addPrefixRoute(DumperRoute $route)
+    {
+        $prefix = $route->getRoute()->compile()->getStaticPrefix();
+
+        // Same prefix, add to current leave
+        if ($this->prefix === $prefix) {
+            $this->add($route);
+
+            return $this;
+        }
+
+        // Prefix starts with route's prefix
+        if ('' === $this->prefix || 0 === strpos($prefix, $this->prefix)) {
+            $collection = new DumperPrefixCollection();
+            $collection->setPrefix(substr($prefix, 0, strlen($this->prefix)+1));
+            $this->add($collection);
+
+            return $collection->addPrefixRoute($route);
+        }
+
+        // No match, fallback to parent (recursively)
+
+        if (null === $parent = $this->getParent()) {
+            throw new \LogicException("The collection root must not have a prefix");
+        }
+
+        return $parent->addPrefixRoute($route);
+    }
+
+    /**
+     * Merges nodes whose prefix ends with a slash
+     *
+     * Children of a node whose prefix ends with a slash are moved to the parent node
+     */
+    public function mergeSlashNodes()
+    {
+        $children = array();
+
+        foreach ($this as $child) {
+            if ($child instanceof self) {
+                $child->mergeSlashNodes();
+                if ('/' === substr($child->prefix, -1)) {
+                    $children = array_merge($children, $child->all());
+                } else {
+                    $children[] = $child;
+                }
+            } else {
+                $children[] = $child;
+            }
+        }
+
+        $this->setAll($children);
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/DumperRoute.php b/vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/DumperRoute.php
new file mode 100644 (file)
index 0000000..2928cdc
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Matcher\Dumper;
+
+use Symfony\Component\Routing\Route;
+
+/**
+ * Container for a Route.
+ *
+ * @author Arnaud Le Blanc <arnaud.lb@gmail.com>
+ */
+class DumperRoute
+{
+    /**
+     * @var string
+     */
+    private $name;
+
+    /**
+     * @var Route
+     */
+    private $route;
+
+    /**
+     * Constructor.
+     *
+     * @param string $name  The route name
+     * @param Route  $route The route
+     */
+    public function __construct($name, Route $route)
+    {
+        $this->name = $name;
+        $this->route = $route;
+    }
+
+    /**
+     * Returns the route name.
+     *
+     * @return string The route name
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * Returns the route.
+     *
+     * @return Route The route
+     */
+    public function getRoute()
+    {
+        return $this->route;
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/MatcherDumper.php b/vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/MatcherDumper.php
new file mode 100644 (file)
index 0000000..52edc01
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Matcher\Dumper;
+
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * MatcherDumper is the abstract class for all built-in matcher dumpers.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+abstract class MatcherDumper implements MatcherDumperInterface
+{
+    /**
+     * @var RouteCollection
+     */
+    private $routes;
+
+    /**
+     * Constructor.
+     *
+     * @param RouteCollection $routes The RouteCollection to dump
+     */
+    public function __construct(RouteCollection $routes)
+    {
+        $this->routes = $routes;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getRoutes()
+    {
+        return $this->routes;
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/MatcherDumperInterface.php b/vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/MatcherDumperInterface.php
new file mode 100644 (file)
index 0000000..f85e4ce
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Matcher\Dumper;
+
+/**
+ * MatcherDumperInterface is the interface that all matcher dumper classes must implement.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface MatcherDumperInterface
+{
+    /**
+     * Dumps a set of routes to a string representation of executable code
+     * that can then be used to match a request against these routes.
+     *
+     * @param array $options An array of options
+     *
+     * @return string Executable code
+     */
+    public function dump(array $options = array());
+
+    /**
+     * Gets the routes to dump.
+     *
+     * @return RouteCollection A RouteCollection instance
+     */
+    public function getRoutes();
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php b/vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php
new file mode 100644 (file)
index 0000000..dc17ffb
--- /dev/null
@@ -0,0 +1,378 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Matcher\Dumper;
+
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * PhpMatcherDumper creates a PHP class able to match URLs for a given set of routes.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Tobias Schultze <http://tobion.de>
+ * @author Arnaud Le Blanc <arnaud.lb@gmail.com>
+ */
+class PhpMatcherDumper extends MatcherDumper
+{
+    /**
+     * Dumps a set of routes to a PHP class.
+     *
+     * Available options:
+     *
+     *  * class:      The class name
+     *  * base_class: The base class name
+     *
+     * @param array $options An array of options
+     *
+     * @return string A PHP class representing the matcher class
+     */
+    public function dump(array $options = array())
+    {
+        $options = array_replace(array(
+            'class'      => 'ProjectUrlMatcher',
+            'base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
+        ), $options);
+
+        // trailing slash support is only enabled if we know how to redirect the user
+        $interfaces = class_implements($options['base_class']);
+        $supportsRedirections = isset($interfaces['Symfony\\Component\\Routing\\Matcher\\RedirectableUrlMatcherInterface']);
+
+        return <<<EOF
+<?php
+
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\RequestContext;
+
+/**
+ * {$options['class']}
+ *
+ * This class has been auto-generated
+ * by the Symfony Routing Component.
+ */
+class {$options['class']} extends {$options['base_class']}
+{
+    /**
+     * Constructor.
+     */
+    public function __construct(RequestContext \$context)
+    {
+        \$this->context = \$context;
+    }
+
+{$this->generateMatchMethod($supportsRedirections)}
+}
+
+EOF;
+    }
+
+    /**
+     * Generates the code for the match method implementing UrlMatcherInterface.
+     *
+     * @param Boolean $supportsRedirections Whether redirections are supported by the base class
+     *
+     * @return string Match method as PHP code
+     */
+    private function generateMatchMethod($supportsRedirections)
+    {
+        $code = rtrim($this->compileRoutes($this->getRoutes(), $supportsRedirections), "\n");
+
+        return <<<EOF
+    public function match(\$pathinfo)
+    {
+        \$allow = array();
+        \$pathinfo = rawurldecode(\$pathinfo);
+
+$code
+
+        throw 0 < count(\$allow) ? new MethodNotAllowedException(array_unique(\$allow)) : new ResourceNotFoundException();
+    }
+EOF;
+    }
+
+    /**
+     * Generates PHP code to match a RouteCollection with all its routes.
+     *
+     * @param RouteCollection $routes               A RouteCollection instance
+     * @param Boolean         $supportsRedirections Whether redirections are supported by the base class
+     *
+     * @return string PHP code
+     */
+    private function compileRoutes(RouteCollection $routes, $supportsRedirections)
+    {
+        $fetchedHost = false;
+
+        $groups = $this->groupRoutesByHostRegex($routes);
+        $code = '';
+
+        foreach ($groups as $collection) {
+            if (null !== $regex = $collection->getAttribute('host_regex')) {
+                if (!$fetchedHost) {
+                    $code .= "        \$host = \$this->context->getHost();\n\n";
+                    $fetchedHost = true;
+                }
+
+                $code .= sprintf("        if (preg_match(%s, \$host, \$hostMatches)) {\n", var_export($regex, true));
+            }
+
+            $tree = $this->buildPrefixTree($collection);
+            $groupCode = $this->compilePrefixRoutes($tree, $supportsRedirections);
+
+            if (null !== $regex) {
+                // apply extra indention at each line (except empty ones)
+                $groupCode = preg_replace('/^.{2,}$/m', '    $0', $groupCode);
+                $code .= $groupCode;
+                $code .= "        }\n\n";
+            } else {
+                $code .= $groupCode;
+            }
+        }
+
+        return $code;
+    }
+
+    /**
+     * Generates PHP code recursively to match a tree of routes
+     *
+     * @param DumperPrefixCollection $collection           A DumperPrefixCollection instance
+     * @param Boolean                $supportsRedirections Whether redirections are supported by the base class
+     * @param string                 $parentPrefix         Prefix of the parent collection
+     *
+     * @return string PHP code
+     */
+    private function compilePrefixRoutes(DumperPrefixCollection $collection, $supportsRedirections, $parentPrefix = '')
+    {
+        $code = '';
+        $prefix = $collection->getPrefix();
+        $optimizable = 1 < strlen($prefix) && 1 < count($collection->all());
+        $optimizedPrefix = $parentPrefix;
+
+        if ($optimizable) {
+            $optimizedPrefix = $prefix;
+
+            $code .= sprintf("    if (0 === strpos(\$pathinfo, %s)) {\n", var_export($prefix, true));
+        }
+
+        foreach ($collection as $route) {
+            if ($route instanceof DumperCollection) {
+                $code .= $this->compilePrefixRoutes($route, $supportsRedirections, $optimizedPrefix);
+            } else {
+                $code .= $this->compileRoute($route->getRoute(), $route->getName(), $supportsRedirections, $optimizedPrefix)."\n";
+            }
+        }
+
+        if ($optimizable) {
+            $code .= "    }\n\n";
+            // apply extra indention at each line (except empty ones)
+            $code = preg_replace('/^.{2,}$/m', '    $0', $code);
+        }
+
+        return $code;
+    }
+
+    /**
+     * Compiles a single Route to PHP code used to match it against the path info.
+     *
+     * @param Route       $route                A Route instance
+     * @param string      $name                 The name of the Route
+     * @param Boolean     $supportsRedirections Whether redirections are supported by the base class
+     * @param string|null $parentPrefix         The prefix of the parent collection used to optimize the code
+     *
+     * @return string PHP code
+     *
+     * @throws \LogicException
+     */
+    private function compileRoute(Route $route, $name, $supportsRedirections, $parentPrefix = null)
+    {
+        $code = '';
+        $compiledRoute = $route->compile();
+        $conditions = array();
+        $hasTrailingSlash = false;
+        $matches = false;
+        $hostMatches = false;
+        $methods = array();
+
+        if ($req = $route->getRequirement('_method')) {
+            $methods = explode('|', strtoupper($req));
+            // GET and HEAD are equivalent
+            if (in_array('GET', $methods) && !in_array('HEAD', $methods)) {
+                $methods[] = 'HEAD';
+            }
+        }
+
+        $supportsTrailingSlash = $supportsRedirections && (!$methods || in_array('HEAD', $methods));
+
+        if (!count($compiledRoute->getPathVariables()) && false !== preg_match('#^(.)\^(?P<url>.*?)\$\1#', $compiledRoute->getRegex(), $m)) {
+            if ($supportsTrailingSlash && substr($m['url'], -1) === '/') {
+                $conditions[] = sprintf("rtrim(\$pathinfo, '/') === %s", var_export(rtrim(str_replace('\\', '', $m['url']), '/'), true));
+                $hasTrailingSlash = true;
+            } else {
+                $conditions[] = sprintf("\$pathinfo === %s", var_export(str_replace('\\', '', $m['url']), true));
+            }
+        } else {
+            if ($compiledRoute->getStaticPrefix() && $compiledRoute->getStaticPrefix() !== $parentPrefix) {
+                $conditions[] = sprintf("0 === strpos(\$pathinfo, %s)", var_export($compiledRoute->getStaticPrefix(), true));
+            }
+
+            $regex = $compiledRoute->getRegex();
+            if ($supportsTrailingSlash && $pos = strpos($regex, '/$')) {
+                $regex = substr($regex, 0, $pos).'/?$'.substr($regex, $pos + 2);
+                $hasTrailingSlash = true;
+            }
+            $conditions[] = sprintf("preg_match(%s, \$pathinfo, \$matches)", var_export($regex, true));
+
+            $matches = true;
+        }
+
+        if ($compiledRoute->getHostVariables()) {
+            $hostMatches = true;
+        }
+
+        $conditions = implode(' && ', $conditions);
+
+        $code .= <<<EOF
+        // $name
+        if ($conditions) {
+
+EOF;
+
+        if ($methods) {
+            $gotoname = 'not_'.preg_replace('/[^A-Za-z0-9_]/', '', $name);
+
+            if (1 === count($methods)) {
+                $code .= <<<EOF
+            if (\$this->context->getMethod() != '$methods[0]') {
+                \$allow[] = '$methods[0]';
+                goto $gotoname;
+            }
+
+
+EOF;
+            } else {
+                $methods = implode("', '", $methods);
+                $code .= <<<EOF
+            if (!in_array(\$this->context->getMethod(), array('$methods'))) {
+                \$allow = array_merge(\$allow, array('$methods'));
+                goto $gotoname;
+            }
+
+
+EOF;
+            }
+        }
+
+        if ($hasTrailingSlash) {
+            $code .= <<<EOF
+            if (substr(\$pathinfo, -1) !== '/') {
+                return \$this->redirect(\$pathinfo.'/', '$name');
+            }
+
+
+EOF;
+        }
+
+        if ($scheme = $route->getRequirement('_scheme')) {
+            if (!$supportsRedirections) {
+                throw new \LogicException('The "_scheme" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.');
+            }
+
+            $code .= <<<EOF
+            if (\$this->context->getScheme() !== '$scheme') {
+                return \$this->redirect(\$pathinfo, '$name', '$scheme');
+            }
+
+
+EOF;
+        }
+
+        // optimize parameters array
+        if ($matches || $hostMatches) {
+            $vars = array();
+            if ($hostMatches) {
+                $vars[] = '$hostMatches';
+            }
+            if ($matches) {
+                $vars[] = '$matches';
+            }
+            $vars[] = "array('_route' => '$name')";
+
+            $code .= sprintf("            return \$this->mergeDefaults(array_replace(%s), %s);\n"
+                , implode(', ', $vars), str_replace("\n", '', var_export($route->getDefaults(), true)));
+
+        } elseif ($route->getDefaults()) {
+            $code .= sprintf("            return %s;\n", str_replace("\n", '', var_export(array_replace($route->getDefaults(), array('_route' => $name)), true)));
+        } else {
+            $code .= sprintf("            return array('_route' => '%s');\n", $name);
+        }
+        $code .= "        }\n";
+
+        if ($methods) {
+            $code .= "        $gotoname:\n";
+        }
+
+        return $code;
+    }
+
+    /**
+     * Groups consecutive routes having the same host regex.
+     *
+     * The result is a collection of collections of routes having the same host regex.
+     *
+     * @param RouteCollection $routes A flat RouteCollection
+     *
+     * @return DumperCollection A collection with routes grouped by host regex in sub-collections
+     */
+    private function groupRoutesByHostRegex(RouteCollection $routes)
+    {
+        $groups = new DumperCollection();
+
+        $currentGroup = new DumperCollection();
+        $currentGroup->setAttribute('host_regex', null);
+        $groups->add($currentGroup);
+
+        foreach ($routes as $name => $route) {
+            $hostRegex = $route->compile()->getHostRegex();
+            if ($currentGroup->getAttribute('host_regex') !== $hostRegex) {
+                $currentGroup = new DumperCollection();
+                $currentGroup->setAttribute('host_regex', $hostRegex);
+                $groups->add($currentGroup);
+            }
+            $currentGroup->add(new DumperRoute($name, $route));
+        }
+
+        return $groups;
+    }
+
+    /**
+     * Organizes the routes into a prefix tree.
+     *
+     * Routes order is preserved such that traversing the tree will traverse the
+     * routes in the origin order.
+     *
+     * @param DumperCollection $collection A collection of routes
+     *
+     * @return DumperPrefixCollection
+     */
+    private function buildPrefixTree(DumperCollection $collection)
+    {
+        $tree = new DumperPrefixCollection();
+        $current = $tree;
+
+        foreach ($collection as $route) {
+            $current = $current->addPrefixRoute($route);
+        }
+
+        $tree->mergeSlashNodes();
+
+        return $tree;
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Matcher/RedirectableUrlMatcher.php b/vendor/symfony/routing/Symfony/Component/Routing/Matcher/RedirectableUrlMatcher.php
new file mode 100644 (file)
index 0000000..51e8005
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Matcher;
+
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\Route;
+
+/**
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+abstract class RedirectableUrlMatcher extends UrlMatcher implements RedirectableUrlMatcherInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function match($pathinfo)
+    {
+        try {
+            $parameters = parent::match($pathinfo);
+        } catch (ResourceNotFoundException $e) {
+            if ('/' === substr($pathinfo, -1) || !in_array($this->context->getMethod(), array('HEAD', 'GET'))) {
+                throw $e;
+            }
+
+            try {
+                parent::match($pathinfo.'/');
+
+                return $this->redirect($pathinfo.'/', null);
+            } catch (ResourceNotFoundException $e2) {
+                throw $e;
+            }
+        }
+
+        return $parameters;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected function handleRouteRequirements($pathinfo, $name, Route $route)
+    {
+        // check HTTP scheme requirement
+        $scheme = $route->getRequirement('_scheme');
+        if ($scheme && $this->context->getScheme() !== $scheme) {
+            return array(self::ROUTE_MATCH, $this->redirect($pathinfo, $name, $scheme));
+        }
+
+        return array(self::REQUIREMENT_MATCH, null);
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Matcher/RedirectableUrlMatcherInterface.php b/vendor/symfony/routing/Symfony/Component/Routing/Matcher/RedirectableUrlMatcherInterface.php
new file mode 100644 (file)
index 0000000..ea91e07
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Matcher;
+
+/**
+ * RedirectableUrlMatcherInterface knows how to redirect the user.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+interface RedirectableUrlMatcherInterface
+{
+    /**
+     * Redirects the user to another URL.
+     *
+     * @param string      $path   The path info to redirect to.
+     * @param string      $route  The route name that matched
+     * @param string|null $scheme The URL scheme (null to keep the current one)
+     *
+     * @return array An array of parameters
+     *
+     * @api
+     */
+    public function redirect($path, $route, $scheme = null);
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Matcher/RequestMatcherInterface.php b/vendor/symfony/routing/Symfony/Component/Routing/Matcher/RequestMatcherInterface.php
new file mode 100644 (file)
index 0000000..b5def3d
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Matcher;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+
+/**
+ * RequestMatcherInterface is the interface that all request matcher classes must implement.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface RequestMatcherInterface
+{
+    /**
+     * Tries to match a request with a set of routes.
+     *
+     * If the matcher can not find information, it must throw one of the exceptions documented
+     * below.
+     *
+     * @param Request $request The request to match
+     *
+     * @return array An array of parameters
+     *
+     * @throws ResourceNotFoundException If no matching resource could be found
+     * @throws MethodNotAllowedException If a matching resource was found but the request method is not allowed
+     */
+    public function matchRequest(Request $request);
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Matcher/TraceableUrlMatcher.php b/vendor/symfony/routing/Symfony/Component/Routing/Matcher/TraceableUrlMatcher.php
new file mode 100644 (file)
index 0000000..c09f83e
--- /dev/null
@@ -0,0 +1,121 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Matcher;
+
+use Symfony\Component\Routing\Exception\ExceptionInterface;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Matcher\UrlMatcher;
+
+/**
+ * TraceableUrlMatcher helps debug path info matching by tracing the match.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class TraceableUrlMatcher extends UrlMatcher
+{
+    const ROUTE_DOES_NOT_MATCH = 0;
+    const ROUTE_ALMOST_MATCHES = 1;
+    const ROUTE_MATCHES        = 2;
+
+    protected $traces;
+
+    public function getTraces($pathinfo)
+    {
+        $this->traces = array();
+
+        try {
+            $this->match($pathinfo);
+        } catch (ExceptionInterface $e) {
+        }
+
+        return $this->traces;
+    }
+
+    protected function matchCollection($pathinfo, RouteCollection $routes)
+    {
+        foreach ($routes as $name => $route) {
+            $compiledRoute = $route->compile();
+
+            if (!preg_match($compiledRoute->getRegex(), $pathinfo, $matches)) {
+                // does it match without any requirements?
+                $r = new Route($route->getPath(), $route->getDefaults(), array(), $route->getOptions());
+                $cr = $r->compile();
+                if (!preg_match($cr->getRegex(), $pathinfo)) {
+                    $this->addTrace(sprintf('Path "%s" does not match', $route->getPath()), self::ROUTE_DOES_NOT_MATCH, $name, $route);
+
+                    continue;
+                }
+
+                foreach ($route->getRequirements() as $n => $regex) {
+                    $r = new Route($route->getPath(), $route->getDefaults(), array($n => $regex), $route->getOptions());
+                    $cr = $r->compile();
+
+                    if (in_array($n, $cr->getVariables()) && !preg_match($cr->getRegex(), $pathinfo)) {
+                        $this->addTrace(sprintf('Requirement for "%s" does not match (%s)', $n, $regex), self::ROUTE_ALMOST_MATCHES, $name, $route);
+
+                        continue 2;
+                    }
+                }
+
+                continue;
+            }
+
+            // check host requirement
+            $hostMatches = array();
+            if ($compiledRoute->getHostRegex() && !preg_match($compiledRoute->getHostRegex(), $this->context->getHost(), $hostMatches)) {
+                $this->addTrace(sprintf('Host "%s" does not match the requirement ("%s")', $this->context->getHost(), $route->getHost()), self::ROUTE_ALMOST_MATCHES, $name, $route);
+
+                return true;
+            }
+
+            // check HTTP method requirement
+            if ($req = $route->getRequirement('_method')) {
+                // HEAD and GET are equivalent as per RFC
+                if ('HEAD' === $method = $this->context->getMethod()) {
+                    $method = 'GET';
+                }
+
+                if (!in_array($method, $req = explode('|', strtoupper($req)))) {
+                    $this->allow = array_merge($this->allow, $req);
+
+                    $this->addTrace(sprintf('Method "%s" does not match the requirement ("%s")', $this->context->getMethod(), implode(', ', $req)), self::ROUTE_ALMOST_MATCHES, $name, $route);
+
+                    continue;
+                }
+            }
+
+            // check HTTP scheme requirement
+            if ($scheme = $route->getRequirement('_scheme')) {
+                if ($this->context->getScheme() !== $scheme) {
+                    $this->addTrace(sprintf('Scheme "%s" does not match the requirement ("%s"); the user will be redirected', $this->context->getScheme(), $scheme), self::ROUTE_ALMOST_MATCHES, $name, $route);
+
+                    return true;
+                }
+            }
+
+            $this->addTrace('Route matches!', self::ROUTE_MATCHES, $name, $route);
+
+            return true;
+        }
+    }
+
+    private function addTrace($log, $level = self::ROUTE_DOES_NOT_MATCH, $name = null, $route = null)
+    {
+        $this->traces[] = array(
+            'log'   => $log,
+            'name'  => $name,
+            'level' => $level,
+            'path'  => null !== $route ? $route->getPath() : null,
+        );
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Matcher/UrlMatcher.php b/vendor/symfony/routing/Symfony/Component/Routing/Matcher/UrlMatcher.php
new file mode 100644 (file)
index 0000000..db18ec4
--- /dev/null
@@ -0,0 +1,208 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Matcher;
+
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\Route;
+
+/**
+ * UrlMatcher matches URL based on a set of routes.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class UrlMatcher implements UrlMatcherInterface
+{
+    const REQUIREMENT_MATCH     = 0;
+    const REQUIREMENT_MISMATCH  = 1;
+    const ROUTE_MATCH           = 2;
+
+    /**
+     * @var RequestContext
+     */
+    protected $context;
+
+    /**
+     * @var array
+     */
+    protected $allow = array();
+
+    /**
+     * @var RouteCollection
+     */
+    protected $routes;
+
+    /**
+     * Constructor.
+     *
+     * @param RouteCollection $routes  A RouteCollection instance
+     * @param RequestContext  $context The context
+     *
+     * @api
+     */
+    public function __construct(RouteCollection $routes, RequestContext $context)
+    {
+        $this->routes = $routes;
+        $this->context = $context;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setContext(RequestContext $context)
+    {
+        $this->context = $context;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getContext()
+    {
+        return $this->context;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function match($pathinfo)
+    {
+        $this->allow = array();
+
+        if ($ret = $this->matchCollection(rawurldecode($pathinfo), $this->routes)) {
+            return $ret;
+        }
+
+        throw 0 < count($this->allow)
+            ? new MethodNotAllowedException(array_unique(array_map('strtoupper', $this->allow)))
+            : new ResourceNotFoundException();
+    }
+
+    /**
+     * Tries to match a URL with a set of routes.
+     *
+     * @param string          $pathinfo The path info to be parsed
+     * @param RouteCollection $routes   The set of routes
+     *
+     * @return array An array of parameters
+     *
+     * @throws ResourceNotFoundException If the resource could not be found
+     * @throws MethodNotAllowedException If the resource was found but the request method is not allowed
+     */
+    protected function matchCollection($pathinfo, RouteCollection $routes)
+    {
+        foreach ($routes as $name => $route) {
+            $compiledRoute = $route->compile();
+
+            // check the static prefix of the URL first. Only use the more expensive preg_match when it matches
+            if ('' !== $compiledRoute->getStaticPrefix() && 0 !== strpos($pathinfo, $compiledRoute->getStaticPrefix())) {
+                continue;
+            }
+
+            if (!preg_match($compiledRoute->getRegex(), $pathinfo, $matches)) {
+                continue;
+            }
+
+            $hostMatches = array();
+            if ($compiledRoute->getHostRegex() && !preg_match($compiledRoute->getHostRegex(), $this->context->getHost(), $hostMatches)) {
+                continue;
+            }
+
+            // check HTTP method requirement
+            if ($req = $route->getRequirement('_method')) {
+                // HEAD and GET are equivalent as per RFC
+                if ('HEAD' === $method = $this->context->getMethod()) {
+                    $method = 'GET';
+                }
+
+                if (!in_array($method, $req = explode('|', strtoupper($req)))) {
+                    $this->allow = array_merge($this->allow, $req);
+
+                    continue;
+                }
+            }
+
+            $status = $this->handleRouteRequirements($pathinfo, $name, $route);
+
+            if (self::ROUTE_MATCH === $status[0]) {
+                return $status[1];
+            }
+
+            if (self::REQUIREMENT_MISMATCH === $status[0]) {
+                continue;
+            }
+
+            return $this->getAttributes($route, $name, array_replace($matches, $hostMatches));
+        }
+    }
+
+    /**
+     * Returns an array of values to use as request attributes.
+     *
+     * As this method requires the Route object, it is not available
+     * in matchers that do not have access to the matched Route instance
+     * (like the PHP and Apache matcher dumpers).
+     *
+     * @param Route  $route      The route we are matching against
+     * @param string $name       The name of the route
+     * @param array  $attributes An array of attributes from the matcher
+     *
+     * @return array An array of parameters
+     */
+    protected function getAttributes(Route $route, $name, array $attributes)
+    {
+        $attributes['_route'] = $name;
+
+        return $this->mergeDefaults($attributes, $route->getDefaults());
+    }
+
+    /**
+     * Handles specific route requirements.
+     *
+     * @param string $pathinfo The path
+     * @param string $name     The route name
+     * @param Route  $route    The route
+     *
+     * @return array The first element represents the status, the second contains additional information
+     */
+    protected function handleRouteRequirements($pathinfo, $name, Route $route)
+    {
+        // check HTTP scheme requirement
+        $scheme = $route->getRequirement('_scheme');
+        $status = $scheme && $scheme !== $this->context->getScheme() ? self::REQUIREMENT_MISMATCH : self::REQUIREMENT_MATCH;
+
+        return array($status, null);
+    }
+
+    /**
+     * Get merged default parameters.
+     *
+     * @param array $params   The parameters
+     * @param array $defaults The defaults
+     *
+     * @return array Merged default parameters
+     */
+    protected function mergeDefaults($params, $defaults)
+    {
+        foreach ($params as $key => $value) {
+            if (!is_int($key)) {
+                $defaults[$key] = $value;
+            }
+        }
+
+        return $defaults;
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Matcher/UrlMatcherInterface.php b/vendor/symfony/routing/Symfony/Component/Routing/Matcher/UrlMatcherInterface.php
new file mode 100644 (file)
index 0000000..dd718b1
--- /dev/null
@@ -0,0 +1,43 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Matcher;
+
+use Symfony\Component\Routing\RequestContextAwareInterface;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+
+/**
+ * UrlMatcherInterface is the interface that all URL matcher classes must implement.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+interface UrlMatcherInterface extends RequestContextAwareInterface
+{
+    /**
+     * Tries to match a URL path with a set of routes.
+     *
+     * If the matcher can not find information, it must throw one of the exceptions documented
+     * below.
+     *
+     * @param string $pathinfo The path info to be parsed (raw format, i.e. not urldecoded)
+     *
+     * @return array An array of parameters
+     *
+     * @throws ResourceNotFoundException If the resource could not be found
+     * @throws MethodNotAllowedException If the resource was found but the request method is not allowed
+     *
+     * @api
+     */
+    public function match($pathinfo);
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/README.md b/vendor/symfony/routing/Symfony/Component/Routing/README.md
new file mode 100644 (file)
index 0000000..663844a
--- /dev/null
@@ -0,0 +1,34 @@
+Routing Component
+=================
+
+Routing associates a request with the code that will convert it to a response.
+
+The example below demonstrates how you can set up a fully working routing
+system:
+
+    use Symfony\Component\HttpFoundation\Request;
+    use Symfony\Component\Routing\Matcher\UrlMatcher;
+    use Symfony\Component\Routing\RequestContext;
+    use Symfony\Component\Routing\RouteCollection;
+    use Symfony\Component\Routing\Route;
+
+    $routes = new RouteCollection();
+    $routes->add('hello', new Route('/hello', array('controller' => 'foo')));
+
+    $context = new RequestContext();
+
+    // this is optional and can be done without a Request instance
+    $context->fromRequest(Request::createFromGlobals());
+
+    $matcher = new UrlMatcher($routes, $context);
+
+    $parameters = $matcher->match('/hello');
+
+Resources
+---------
+
+You can run the unit tests with the following command:
+
+    $ cd path/to/Symfony/Component/Routing/
+    $ composer.phar install --dev
+    $ phpunit
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/RequestContext.php b/vendor/symfony/routing/Symfony/Component/Routing/RequestContext.php
new file mode 100644 (file)
index 0000000..cb53696
--- /dev/null
@@ -0,0 +1,315 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing;
+
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Holds information about the current request.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class RequestContext
+{
+    private $baseUrl;
+    private $pathInfo;
+    private $method;
+    private $host;
+    private $scheme;
+    private $httpPort;
+    private $httpsPort;
+    private $queryString;
+
+    /**
+     * @var array
+     */
+    private $parameters = array();
+
+    /**
+     * Constructor.
+     *
+     * @param string  $baseUrl      The base URL
+     * @param string  $method       The HTTP method
+     * @param string  $host         The HTTP host name
+     * @param string  $scheme       The HTTP scheme
+     * @param integer $httpPort     The HTTP port
+     * @param integer $httpsPort    The HTTPS port
+     * @param string  $path         The path
+     * @param string  $queryString  The query string
+     *
+     * @api
+     */
+    public function __construct($baseUrl = '', $method = 'GET', $host = 'localhost', $scheme = 'http', $httpPort = 80, $httpsPort = 443, $path = '/', $queryString = '')
+    {
+        $this->baseUrl = $baseUrl;
+        $this->method = strtoupper($method);
+        $this->host = $host;
+        $this->scheme = strtolower($scheme);
+        $this->httpPort = $httpPort;
+        $this->httpsPort = $httpsPort;
+        $this->pathInfo = $path;
+        $this->queryString = $queryString;
+    }
+
+    public function fromRequest(Request $request)
+    {
+        $this->setBaseUrl($request->getBaseUrl());
+        $this->setPathInfo($request->getPathInfo());
+        $this->setMethod($request->getMethod());
+        $this->setHost($request->getHost());
+        $this->setScheme($request->getScheme());
+        $this->setHttpPort($request->isSecure() ? $this->httpPort : $request->getPort());
+        $this->setHttpsPort($request->isSecure() ? $request->getPort() : $this->httpsPort);
+        $this->setQueryString($request->server->get('QUERY_STRING'));
+    }
+
+    /**
+     * Gets the base URL.
+     *
+     * @return string The base URL
+     */
+    public function getBaseUrl()
+    {
+        return $this->baseUrl;
+    }
+
+    /**
+     * Sets the base URL.
+     *
+     * @param string $baseUrl The base URL
+     *
+     * @api
+     */
+    public function setBaseUrl($baseUrl)
+    {
+        $this->baseUrl = $baseUrl;
+    }
+
+    /**
+     * Gets the path info.
+     *
+     * @return string The path info
+     */
+    public function getPathInfo()
+    {
+        return $this->pathInfo;
+    }
+
+    /**
+     * Sets the path info.
+     *
+     * @param string $pathInfo The path info
+     */
+    public function setPathInfo($pathInfo)
+    {
+        $this->pathInfo = $pathInfo;
+    }
+
+    /**
+     * Gets the HTTP method.
+     *
+     * The method is always an uppercased string.
+     *
+     * @return string The HTTP method
+     */
+    public function getMethod()
+    {
+        return $this->method;
+    }
+
+    /**
+     * Sets the HTTP method.
+     *
+     * @param string $method The HTTP method
+     *
+     * @api
+     */
+    public function setMethod($method)
+    {
+        $this->method = strtoupper($method);
+    }
+
+    /**
+     * Gets the HTTP host.
+     *
+     * @return string The HTTP host
+     */
+    public function getHost()
+    {
+        return $this->host;
+    }
+
+    /**
+     * Sets the HTTP host.
+     *
+     * @param string $host The HTTP host
+     *
+     * @api
+     */
+    public function setHost($host)
+    {
+        $this->host = $host;
+    }
+
+    /**
+     * Gets the HTTP scheme.
+     *
+     * @return string The HTTP scheme
+     */
+    public function getScheme()
+    {
+        return $this->scheme;
+    }
+
+    /**
+     * Sets the HTTP scheme.
+     *
+     * @param string $scheme The HTTP scheme
+     *
+     * @api
+     */
+    public function setScheme($scheme)
+    {
+        $this->scheme = strtolower($scheme);
+    }
+
+    /**
+     * Gets the HTTP port.
+     *
+     * @return string The HTTP port
+     */
+    public function getHttpPort()
+    {
+        return $this->httpPort;
+    }
+
+    /**
+     * Sets the HTTP port.
+     *
+     * @param string $httpPort The HTTP port
+     *
+     * @api
+     */
+    public function setHttpPort($httpPort)
+    {
+        $this->httpPort = $httpPort;
+    }
+
+    /**
+     * Gets the HTTPS port.
+     *
+     * @return string The HTTPS port
+     */
+    public function getHttpsPort()
+    {
+        return $this->httpsPort;
+    }
+
+    /**
+     * Sets the HTTPS port.
+     *
+     * @param string $httpsPort The HTTPS port
+     *
+     * @api
+     */
+    public function setHttpsPort($httpsPort)
+    {
+        $this->httpsPort = $httpsPort;
+    }
+
+    /**
+     * Gets the query string.
+     *
+     * @return string The query string
+     */
+    public function getQueryString()
+    {
+        return $this->queryString;
+    }
+
+    /**
+     * Sets the query string.
+     *
+     * @param string $queryString The query string
+     *
+     * @api
+     */
+    public function setQueryString($queryString)
+    {
+        $this->queryString = $queryString;
+    }
+
+    /**
+     * Returns the parameters.
+     *
+     * @return array The parameters
+     */
+    public function getParameters()
+    {
+        return $this->parameters;
+    }
+
+    /**
+     * Sets the parameters.
+     *
+     * This method implements a fluent interface.
+     *
+     * @param array $parameters The parameters
+     *
+     * @return Route The current Route instance
+     */
+    public function setParameters(array $parameters)
+    {
+        $this->parameters = $parameters;
+
+        return $this;
+    }
+
+    /**
+     * Gets a parameter value.
+     *
+     * @param string $name A parameter name
+     *
+     * @return mixed The parameter value
+     */
+    public function getParameter($name)
+    {
+        return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
+    }
+
+    /**
+     * Checks if a parameter value is set for the given parameter.
+     *
+     * @param string $name A parameter name
+     *
+     * @return Boolean true if the parameter value is set, false otherwise
+     */
+    public function hasParameter($name)
+    {
+        return array_key_exists($name, $this->parameters);
+    }
+
+    /**
+     * Sets a parameter value.
+     *
+     * @param string $name      A parameter name
+     * @param mixed  $parameter The parameter value
+     *
+     * @api
+     */
+    public function setParameter($name, $parameter)
+    {
+        $this->parameters[$name] = $parameter;
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/RequestContextAwareInterface.php b/vendor/symfony/routing/Symfony/Component/Routing/RequestContextAwareInterface.php
new file mode 100644 (file)
index 0000000..daf5254
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing;
+
+/**
+ * @api
+ */
+interface RequestContextAwareInterface
+{
+    /**
+     * Sets the request context.
+     *
+     * @param RequestContext $context The context
+     *
+     * @api
+     */
+    public function setContext(RequestContext $context);
+
+    /**
+     * Gets the request context.
+     *
+     * @return RequestContext The context
+     *
+     * @api
+     */
+    public function getContext();
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Route.php b/vendor/symfony/routing/Symfony/Component/Routing/Route.php
new file mode 100644 (file)
index 0000000..060e978
--- /dev/null
@@ -0,0 +1,594 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing;
+
+/**
+ * A Route describes a route and its parameters.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Tobias Schultze <http://tobion.de>
+ *
+ * @api
+ */
+class Route implements \Serializable
+{
+    /**
+     * @var string
+     */
+    private $path = '/';
+
+    /**
+     * @var string
+     */
+    private $host = '';
+
+    /**
+     * @var array
+     */
+    private $schemes = array();
+
+    /**
+     * @var array
+     */
+    private $methods = array();
+
+    /**
+     * @var array
+     */
+    private $defaults = array();
+
+    /**
+     * @var array
+     */
+    private $requirements = array();
+
+    /**
+     * @var array
+     */
+    private $options = array();
+
+    /**
+     * @var null|RouteCompiler
+     */
+    private $compiled;
+
+    /**
+     * Constructor.
+     *
+     * Available options:
+     *
+     *  * compiler_class: A class name able to compile this route instance (RouteCompiler by default)
+     *
+     * @param string       $path         The path pattern to match
+     * @param array        $defaults     An array of default parameter values
+     * @param array        $requirements An array of requirements for parameters (regexes)
+     * @param array        $options      An array of options
+     * @param string       $host         The host pattern to match
+     * @param string|array $schemes      A required URI scheme or an array of restricted schemes
+     * @param string|array $methods      A required HTTP method or an array of restricted methods
+     *
+     * @api
+     */
+    public function __construct($path, array $defaults = array(), array $requirements = array(), array $options = array(), $host = '', $schemes = array(), $methods = array())
+    {
+        $this->setPath($path);
+        $this->setDefaults($defaults);
+        $this->setRequirements($requirements);
+        $this->setOptions($options);
+        $this->setHost($host);
+        // The conditions make sure that an initial empty $schemes/$methods does not override the corresponding requirement.
+        // They can be removed when the BC layer is removed.
+        if ($schemes) {
+            $this->setSchemes($schemes);
+        }
+        if ($methods) {
+            $this->setMethods($methods);
+        }
+    }
+
+    public function serialize()
+    {
+        return serialize(array(
+            'path'         => $this->path,
+            'host'         => $this->host,
+            'defaults'     => $this->defaults,
+            'requirements' => $this->requirements,
+            'options'      => $this->options,
+            'schemes'      => $this->schemes,
+            'methods'      => $this->methods,
+        ));
+    }
+
+    public function unserialize($data)
+    {
+        $data = unserialize($data);
+        $this->path = $data['path'];
+        $this->host = $data['host'];
+        $this->defaults = $data['defaults'];
+        $this->requirements = $data['requirements'];
+        $this->options = $data['options'];
+        $this->schemes = $data['schemes'];
+        $this->methods = $data['methods'];
+    }
+
+    /**
+     * Returns the pattern for the path.
+     *
+     * @return string The pattern
+     *
+     * @deprecated Deprecated in 2.2, to be removed in 3.0. Use getPath instead.
+     */
+    public function getPattern()
+    {
+        return $this->path;
+    }
+
+    /**
+     * Sets the pattern for the path.
+     *
+     * This method implements a fluent interface.
+     *
+     * @param string $pattern The path pattern
+     *
+     * @return Route The current Route instance
+     *
+     * @deprecated Deprecated in 2.2, to be removed in 3.0. Use setPath instead.
+     */
+    public function setPattern($pattern)
+    {
+        return $this->setPath($pattern);
+    }
+
+    /**
+     * Returns the pattern for the path.
+     *
+     * @return string The path pattern
+     */
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    /**
+     * Sets the pattern for the path.
+     *
+     * This method implements a fluent interface.
+     *
+     * @param string $pattern The path pattern
+     *
+     * @return Route The current Route instance
+     */
+    public function setPath($pattern)
+    {
+        // A pattern must start with a slash and must not have multiple slashes at the beginning because the
+        // generated path for this route would be confused with a network path, e.g. '//domain.com/path'.
+        $this->path = '/'.ltrim(trim($pattern), '/');
+        $this->compiled = null;
+
+        return $this;
+    }
+
+    /**
+     * Returns the pattern for the host.
+     *
+     * @return string The host pattern
+     */
+    public function getHost()
+    {
+        return $this->host;
+    }
+
+    /**
+     * Sets the pattern for the host.
+     *
+     * This method implements a fluent interface.
+     *
+     * @param string $pattern The host pattern
+     *
+     * @return Route The current Route instance
+     */
+    public function setHost($pattern)
+    {
+        $this->host = (string) $pattern;
+        $this->compiled = null;
+
+        return $this;
+    }
+
+    /**
+     * Returns the lowercased schemes this route is restricted to.
+     * So an empty array means that any scheme is allowed.
+     *
+     * @return array The schemes
+     */
+    public function getSchemes()
+    {
+        return $this->schemes;
+    }
+
+    /**
+     * Sets the schemes (e.g. 'https') this route is restricted to.
+     * So an empty array means that any scheme is allowed.
+     *
+     * This method implements a fluent interface.
+     *
+     * @param string|array $schemes The scheme or an array of schemes
+     *
+     * @return Route The current Route instance
+     */
+    public function setSchemes($schemes)
+    {
+        $this->schemes = array_map('strtolower', (array) $schemes);
+
+        // this is to keep BC and will be removed in a future version
+        if ($this->schemes) {
+            $this->requirements['_scheme'] = implode('|', $this->schemes);
+        } else {
+            unset($this->requirements['_scheme']);
+        }
+
+        $this->compiled = null;
+
+        return $this;
+    }
+
+    /**
+     * Returns the uppercased HTTP methods this route is restricted to.
+     * So an empty array means that any method is allowed.
+     *
+     * @return array The schemes
+     */
+    public function getMethods()
+    {
+        return $this->methods;
+    }
+
+    /**
+     * Sets the HTTP methods (e.g. 'POST') this route is restricted to.
+     * So an empty array means that any method is allowed.
+     *
+     * This method implements a fluent interface.
+     *
+     * @param string|array $methods The method or an array of methods
+     *
+     * @return Route The current Route instance
+     */
+    public function setMethods($methods)
+    {
+        $this->methods = array_map('strtoupper', (array) $methods);
+
+        // this is to keep BC and will be removed in a future version
+        if ($this->methods) {
+            $this->requirements['_method'] = implode('|', $this->methods);
+        } else {
+            unset($this->requirements['_method']);
+        }
+
+        $this->compiled = null;
+
+        return $this;
+    }
+
+    /**
+     * Returns the options.
+     *
+     * @return array The options
+     */
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
+    /**
+     * Sets the options.
+     *
+     * This method implements a fluent interface.
+     *
+     * @param array $options The options
+     *
+     * @return Route The current Route instance
+     */
+    public function setOptions(array $options)
+    {
+        $this->options = array(
+            'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler',
+        );
+
+        return $this->addOptions($options);
+    }
+
+    /**
+     * Adds options.
+     *
+     * This method implements a fluent interface.
+     *
+     * @param array $options The options
+     *
+     * @return Route The current Route instance
+     */
+    public function addOptions(array $options)
+    {
+        foreach ($options as $name => $option) {
+            $this->options[$name] = $option;
+        }
+        $this->compiled = null;
+
+        return $this;
+    }
+
+    /**
+     * Sets an option value.
+     *
+     * This method implements a fluent interface.
+     *
+     * @param string $name  An option name
+     * @param mixed  $value The option value
+     *
+     * @return Route The current Route instance
+     *
+     * @api
+     */
+    public function setOption($name, $value)
+    {
+        $this->options[$name] = $value;
+        $this->compiled = null;
+
+        return $this;
+    }
+
+    /**
+     * Get an option value.
+     *
+     * @param string $name An option name
+     *
+     * @return mixed The option value or null when not given
+     */
+    public function getOption($name)
+    {
+        return isset($this->options[$name]) ? $this->options[$name] : null;
+    }
+
+    /**
+     * Checks if an option has been set
+     *
+     * @param string $name An option name
+     *
+     * @return Boolean true if the option is set, false otherwise
+     */
+    public function hasOption($name)
+    {
+        return array_key_exists($name, $this->options);
+    }
+
+    /**
+     * Returns the defaults.
+     *
+     * @return array The defaults
+     */
+    public function getDefaults()
+    {
+        return $this->defaults;
+    }
+
+    /**
+     * Sets the defaults.
+     *
+     * This method implements a fluent interface.
+     *
+     * @param array $defaults The defaults
+     *
+     * @return Route The current Route instance
+     */
+    public function setDefaults(array $defaults)
+    {
+        $this->defaults = array();
+
+        return $this->addDefaults($defaults);
+    }
+
+    /**
+     * Adds defaults.
+     *
+     * This method implements a fluent interface.
+     *
+     * @param array $defaults The defaults
+     *
+     * @return Route The current Route instance
+     */
+    public function addDefaults(array $defaults)
+    {
+        foreach ($defaults as $name => $default) {
+            $this->defaults[$name] = $default;
+        }
+        $this->compiled = null;
+
+        return $this;
+    }
+
+    /**
+     * Gets a default value.
+     *
+     * @param string $name A variable name
+     *
+     * @return mixed The default value or null when not given
+     */
+    public function getDefault($name)
+    {
+        return isset($this->defaults[$name]) ? $this->defaults[$name] : null;
+    }
+
+    /**
+     * Checks if a default value is set for the given variable.
+     *
+     * @param string $name A variable name
+     *
+     * @return Boolean true if the default value is set, false otherwise
+     */
+    public function hasDefault($name)
+    {
+        return array_key_exists($name, $this->defaults);
+    }
+
+    /**
+     * Sets a default value.
+     *
+     * @param string $name    A variable name
+     * @param mixed  $default The default value
+     *
+     * @return Route The current Route instance
+     *
+     * @api
+     */
+    public function setDefault($name, $default)
+    {
+        $this->defaults[$name] = $default;
+        $this->compiled = null;
+
+        return $this;
+    }
+
+    /**
+     * Returns the requirements.
+     *
+     * @return array The requirements
+     */
+    public function getRequirements()
+    {
+        return $this->requirements;
+    }
+
+    /**
+     * Sets the requirements.
+     *
+     * This method implements a fluent interface.
+     *
+     * @param array $requirements The requirements
+     *
+     * @return Route The current Route instance
+     */
+    public function setRequirements(array $requirements)
+    {
+        $this->requirements = array();
+
+        return $this->addRequirements($requirements);
+    }
+
+    /**
+     * Adds requirements.
+     *
+     * This method implements a fluent interface.
+     *
+     * @param array $requirements The requirements
+     *
+     * @return Route The current Route instance
+     */
+    public function addRequirements(array $requirements)
+    {
+        foreach ($requirements as $key => $regex) {
+            $this->requirements[$key] = $this->sanitizeRequirement($key, $regex);
+        }
+        $this->compiled = null;
+
+        return $this;
+    }
+
+    /**
+     * Returns the requirement for the given key.
+     *
+     * @param string $key The key
+     *
+     * @return string|null The regex or null when not given
+     */
+    public function getRequirement($key)
+    {
+        return isset($this->requirements[$key]) ? $this->requirements[$key] : null;
+    }
+
+    /**
+     * Checks if a requirement is set for the given key.
+     *
+     * @param string $key A variable name
+     *
+     * @return Boolean true if a requirement is specified, false otherwise
+     */
+    public function hasRequirement($key)
+    {
+        return array_key_exists($key, $this->requirements);
+    }
+
+    /**
+     * Sets a requirement for the given key.
+     *
+     * @param string $key   The key
+     * @param string $regex The regex
+     *
+     * @return Route The current Route instance
+     *
+     * @api
+     */
+    public function setRequirement($key, $regex)
+    {
+        $this->requirements[$key] = $this->sanitizeRequirement($key, $regex);
+        $this->compiled = null;
+
+        return $this;
+    }
+
+    /**
+     * Compiles the route.
+     *
+     * @return CompiledRoute A CompiledRoute instance
+     *
+     * @throws \LogicException If the Route cannot be compiled because the
+     *                         path or host pattern is invalid
+     *
+     * @see RouteCompiler which is responsible for the compilation process
+     */
+    public function compile()
+    {
+        if (null !== $this->compiled) {
+            return $this->compiled;
+        }
+
+        $class = $this->getOption('compiler_class');
+
+        return $this->compiled = $class::compile($this);
+    }
+
+    private function sanitizeRequirement($key, $regex)
+    {
+        if (!is_string($regex)) {
+            throw new \InvalidArgumentException(sprintf('Routing requirement for "%s" must be a string.', $key));
+        }
+
+        if ('' !== $regex && '^' === $regex[0]) {
+            $regex = (string) substr($regex, 1); // returns false for a single character
+        }
+
+        if ('$' === substr($regex, -1)) {
+            $regex = substr($regex, 0, -1);
+        }
+
+        if ('' === $regex) {
+            throw new \InvalidArgumentException(sprintf('Routing requirement for "%s" cannot be empty.', $key));
+        }
+
+        // this is to keep BC and will be removed in a future version
+        if ('_scheme' === $key) {
+            $this->setSchemes(explode('|', $regex));
+        } elseif ('_method' === $key) {
+            $this->setMethods(explode('|', $regex));
+        }
+
+        return $regex;
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/RouteCollection.php b/vendor/symfony/routing/Symfony/Component/Routing/RouteCollection.php
new file mode 100644 (file)
index 0000000..499fe0f
--- /dev/null
@@ -0,0 +1,271 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing;
+
+use Symfony\Component\Config\Resource\ResourceInterface;
+
+/**
+ * A RouteCollection represents a set of Route instances.
+ *
+ * When adding a route at the end of the collection, an existing route
+ * with the same name is removed first. So there can only be one route
+ * with a given name.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Tobias Schultze <http://tobion.de>
+ *
+ * @api
+ */
+class RouteCollection implements \IteratorAggregate, \Countable
+{
+    /**
+     * @var Route[]
+     */
+    private $routes = array();
+
+    /**
+     * @var array
+     */
+    private $resources = array();
+
+    public function __clone()
+    {
+        foreach ($this->routes as $name => $route) {
+            $this->routes[$name] = clone $route;
+        }
+    }
+
+    /**
+     * Gets the current RouteCollection as an Iterator that includes all routes.
+     *
+     * It implements \IteratorAggregate.
+     *
+     * @see all()
+     *
+     * @return \ArrayIterator An \ArrayIterator object for iterating over routes
+     */
+    public function getIterator()
+    {
+        return new \ArrayIterator($this->routes);
+    }
+
+    /**
+     * Gets the number of Routes in this collection.
+     *
+     * @return int The number of routes
+     */
+    public function count()
+    {
+        return count($this->routes);
+    }
+
+    /**
+     * Adds a route.
+     *
+     * @param string $name  The route name
+     * @param Route  $route A Route instance
+     *
+     * @api
+     */
+    public function add($name, Route $route)
+    {
+        unset($this->routes[$name]);
+
+        $this->routes[$name] = $route;
+    }
+
+    /**
+     * Returns all routes in this collection.
+     *
+     * @return Route[] An array of routes
+     */
+    public function all()
+    {
+        return $this->routes;
+    }
+
+    /**
+     * Gets a route by name.
+     *
+     * @param string $name The route name
+     *
+     * @return Route|null A Route instance or null when not found
+     */
+    public function get($name)
+    {
+        return isset($this->routes[$name]) ? $this->routes[$name] : null;
+    }
+
+    /**
+     * Removes a route or an array of routes by name from the collection
+     *
+     * @param string|array $name The route name or an array of route names
+     */
+    public function remove($name)
+    {
+        foreach ((array) $name as $n) {
+            unset($this->routes[$n]);
+        }
+    }
+
+    /**
+     * Adds a route collection at the end of the current set by appending all
+     * routes of the added collection.
+     *
+     * @param RouteCollection $collection      A RouteCollection instance
+     *
+     * @api
+     */
+    public function addCollection(RouteCollection $collection)
+    {
+        // we need to remove all routes with the same names first because just replacing them
+        // would not place the new route at the end of the merged array
+        foreach ($collection->all() as $name => $route) {
+            unset($this->routes[$name]);
+            $this->routes[$name] = $route;
+        }
+
+        $this->resources = array_merge($this->resources, $collection->getResources());
+    }
+
+    /**
+     * Adds a prefix to the path of all child routes.
+     *
+     * @param string $prefix       An optional prefix to add before each pattern of the route collection
+     * @param array  $defaults     An array of default values
+     * @param array  $requirements An array of requirements
+     *
+     * @api
+     */
+    public function addPrefix($prefix, array $defaults = array(), array $requirements = array())
+    {
+        $prefix = trim(trim($prefix), '/');
+
+        if ('' === $prefix) {
+            return;
+        }
+
+        foreach ($this->routes as $route) {
+            $route->setPath('/'.$prefix.$route->getPath());
+            $route->addDefaults($defaults);
+            $route->addRequirements($requirements);
+        }
+    }
+
+    /**
+     * Sets the host pattern on all routes.
+     *
+     * @param string $pattern      The pattern
+     * @param array  $defaults     An array of default values
+     * @param array  $requirements An array of requirements
+     */
+    public function setHost($pattern, array $defaults = array(), array $requirements = array())
+    {
+        foreach ($this->routes as $route) {
+            $route->setHost($pattern);
+            $route->addDefaults($defaults);
+            $route->addRequirements($requirements);
+        }
+    }
+
+    /**
+     * Adds defaults to all routes.
+     *
+     * An existing default value under the same name in a route will be overridden.
+     *
+     * @param array $defaults An array of default values
+     */
+    public function addDefaults(array $defaults)
+    {
+        if ($defaults) {
+            foreach ($this->routes as $route) {
+                $route->addDefaults($defaults);
+            }
+        }
+    }
+
+    /**
+     * Adds requirements to all routes.
+     *
+     * An existing requirement under the same name in a route will be overridden.
+     *
+     * @param array $requirements An array of requirements
+     */
+    public function addRequirements(array $requirements)
+    {
+        if ($requirements) {
+            foreach ($this->routes as $route) {
+                $route->addRequirements($requirements);
+            }
+        }
+    }
+
+    /**
+     * Adds options to all routes.
+     *
+     * An existing option value under the same name in a route will be overridden.
+     *
+     * @param array $options An array of options
+     */
+    public function addOptions(array $options)
+    {
+        if ($options) {
+            foreach ($this->routes as $route) {
+                $route->addOptions($options);
+            }
+        }
+    }
+
+    /**
+     * Sets the schemes (e.g. 'https') all child routes are restricted to.
+     *
+     * @param string|array $schemes The scheme or an array of schemes
+     */
+    public function setSchemes($schemes)
+    {
+        foreach ($this->routes as $route) {
+            $route->setSchemes($schemes);
+        }
+    }
+
+    /**
+     * Sets the HTTP methods (e.g. 'POST') all child routes are restricted to.
+     *
+     * @param string|array $methods The method or an array of methods
+     */
+    public function setMethods($methods)
+    {
+        foreach ($this->routes as $route) {
+            $route->setMethods($methods);
+        }
+    }
+
+    /**
+     * Returns an array of resources loaded to build this collection.
+     *
+     * @return ResourceInterface[] An array of resources
+     */
+    public function getResources()
+    {
+        return array_unique($this->resources);
+    }
+
+    /**
+     * Adds a resource for this collection.
+     *
+     * @param ResourceInterface $resource A resource instance
+     */
+    public function addResource(ResourceInterface $resource)
+    {
+        $this->resources[] = $resource;
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/RouteCompiler.php b/vendor/symfony/routing/Symfony/Component/Routing/RouteCompiler.php
new file mode 100644 (file)
index 0000000..7ced4b3
--- /dev/null
@@ -0,0 +1,233 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing;
+
+/**
+ * RouteCompiler compiles Route instances to CompiledRoute instances.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Tobias Schultze <http://tobion.de>
+ */
+class RouteCompiler implements RouteCompilerInterface
+{
+    const REGEX_DELIMITER = '#';
+
+    /**
+     * This string defines the characters that are automatically considered separators in front of
+     * optional placeholders (with default and no static text following). Such a single separator
+     * can be left out together with the optional placeholder from matching and generating URLs.
+     */
+    const SEPARATORS = '/,;.:-_~+*=@|';
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws \LogicException  If a variable is referenced more than once
+     * @throws \DomainException If a variable name is numeric because PHP raises an error for such
+     *                          subpatterns in PCRE and thus would break matching, e.g. "(?P<123>.+)".
+     */
+    public static function compile(Route $route)
+    {
+        $staticPrefix = null;
+        $hostVariables = array();
+        $pathVariables = array();
+        $variables = array();
+        $tokens = array();
+        $regex = null;
+        $hostRegex = null;
+        $hostTokens = array();
+
+        if ('' !== $host = $route->getHost()) {
+            $result = self::compilePattern($route, $host, true);
+
+            $hostVariables = $result['variables'];
+            $variables = array_merge($variables, $hostVariables);
+
+            $hostTokens = $result['tokens'];
+            $hostRegex = $result['regex'];
+        }
+
+        $path = $route->getPath();
+
+        $result = self::compilePattern($route, $path, false);
+
+        $staticPrefix = $result['staticPrefix'];
+
+        $pathVariables = $result['variables'];
+        $variables = array_merge($variables, $pathVariables);
+
+        $tokens = $result['tokens'];
+        $regex = $result['regex'];
+
+        return new CompiledRoute(
+            $staticPrefix,
+            $regex,
+            $tokens,
+            $pathVariables,
+            $hostRegex,
+            $hostTokens,
+            $hostVariables,
+            array_unique($variables)
+        );
+    }
+
+    private static function compilePattern(Route $route, $pattern, $isHost)
+    {
+        $tokens = array();
+        $variables = array();
+        $matches = array();
+        $pos = 0;
+        $defaultSeparator = $isHost ? '.' : '/';
+
+        // Match all variables enclosed in "{}" and iterate over them. But we only want to match the innermost variable
+        // in case of nested "{}", e.g. {foo{bar}}. This in ensured because \w does not match "{" or "}" itself.
+        preg_match_all('#\{\w+\}#', $pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
+        foreach ($matches as $match) {
+            $varName = substr($match[0][0], 1, -1);
+            // get all static text preceding the current variable
+            $precedingText = substr($pattern, $pos, $match[0][1] - $pos);
+            $pos = $match[0][1] + strlen($match[0][0]);
+            $precedingChar = strlen($precedingText) > 0 ? substr($precedingText, -1) : '';
+            $isSeparator = '' !== $precedingChar && false !== strpos(static::SEPARATORS, $precedingChar);
+
+            if (is_numeric($varName)) {
+                throw new \DomainException(sprintf('Variable name "%s" cannot be numeric in route pattern "%s". Please use a different name.', $varName, $pattern));
+            }
+            if (in_array($varName, $variables)) {
+                throw new \LogicException(sprintf('Route pattern "%s" cannot reference variable name "%s" more than once.', $pattern, $varName));
+            }
+
+            if ($isSeparator && strlen($precedingText) > 1) {
+                $tokens[] = array('text', substr($precedingText, 0, -1));
+            } elseif (!$isSeparator && strlen($precedingText) > 0) {
+                $tokens[] = array('text', $precedingText);
+            }
+
+            $regexp = $route->getRequirement($varName);
+            if (null === $regexp) {
+                $followingPattern = (string) substr($pattern, $pos);
+                // Find the next static character after the variable that functions as a separator. By default, this separator and '/'
+                // are disallowed for the variable. This default requirement makes sure that optional variables can be matched at all
+                // and that the generating-matching-combination of URLs unambiguous, i.e. the params used for generating the URL are
+                // the same that will be matched. Example: new Route('/{page}.{_format}', array('_format' => 'html'))
+                // If {page} would also match the separating dot, {_format} would never match as {page} will eagerly consume everything.
+                // Also even if {_format} was not optional the requirement prevents that {page} matches something that was originally
+                // part of {_format} when generating the URL, e.g. _format = 'mobile.html'.
+                $nextSeparator = self::findNextSeparator($followingPattern);
+                $regexp = sprintf(
+                    '[^%s%s]+',
+                    preg_quote($defaultSeparator, self::REGEX_DELIMITER),
+                    $defaultSeparator !== $nextSeparator && '' !== $nextSeparator ? preg_quote($nextSeparator, self::REGEX_DELIMITER) : ''
+                );
+                if (('' !== $nextSeparator && !preg_match('#^\{\w+\}#', $followingPattern)) || '' === $followingPattern) {
+                    // When we have a separator, which is disallowed for the variable, we can optimize the regex with a possessive
+                    // quantifier. This prevents useless backtracking of PCRE and improves performance by 20% for matching those patterns.
+                    // Given the above example, there is no point in backtracking into {page} (that forbids the dot) when a dot must follow
+                    // after it. This optimization cannot be applied when the next char is no real separator or when the next variable is
+                    // directly adjacent, e.g. '/{x}{y}'.
+                    $regexp .= '+';
+                }
+            }
+
+            $tokens[] = array('variable', $isSeparator ? $precedingChar : '', $regexp, $varName);
+            $variables[] = $varName;
+        }
+
+        if ($pos < strlen($pattern)) {
+            $tokens[] = array('text', substr($pattern, $pos));
+        }
+
+        // find the first optional token
+        $firstOptional = PHP_INT_MAX;
+        if (!$isHost) {
+            for ($i = count($tokens) - 1; $i >= 0; $i--) {
+                $token = $tokens[$i];
+                if ('variable' === $token[0] && $route->hasDefault($token[3])) {
+                    $firstOptional = $i;
+                } else {
+                    break;
+                }
+            }
+        }
+
+        // compute the matching regexp
+        $regexp = '';
+        for ($i = 0, $nbToken = count($tokens); $i < $nbToken; $i++) {
+            $regexp .= self::computeRegexp($tokens, $i, $firstOptional);
+        }
+
+        return array(
+            'staticPrefix' => 'text' === $tokens[0][0] ? $tokens[0][1] : '',
+            'regex' => self::REGEX_DELIMITER.'^'.$regexp.'$'.self::REGEX_DELIMITER.'s',
+            'tokens' => array_reverse($tokens),
+            'variables' => $variables,
+        );
+    }
+
+    /**
+     * Returns the next static character in the Route pattern that will serve as a separator.
+     *
+     * @param string $pattern The route pattern
+     *
+     * @return string The next static character that functions as separator (or empty string when none available)
+     */
+    private static function findNextSeparator($pattern)
+    {
+        if ('' == $pattern) {
+            // return empty string if pattern is empty or false (false which can be returned by substr)
+            return '';
+        }
+        // first remove all placeholders from the pattern so we can find the next real static character
+        $pattern = preg_replace('#\{\w+\}#', '', $pattern);
+
+        return isset($pattern[0]) && false !== strpos(static::SEPARATORS, $pattern[0]) ? $pattern[0] : '';
+    }
+
+    /**
+     * Computes the regexp used to match a specific token. It can be static text or a subpattern.
+     *
+     * @param array   $tokens        The route tokens
+     * @param integer $index         The index of the current token
+     * @param integer $firstOptional The index of the first optional token
+     *
+     * @return string The regexp pattern for a single token
+     */
+    private static function computeRegexp(array $tokens, $index, $firstOptional)
+    {
+        $token = $tokens[$index];
+        if ('text' === $token[0]) {
+            // Text tokens
+            return preg_quote($token[1], self::REGEX_DELIMITER);
+        } else {
+            // Variable tokens
+            if (0 === $index && 0 === $firstOptional) {
+                // When the only token is an optional variable token, the separator is required
+                return sprintf('%s(?P<%s>%s)?', preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]);
+            } else {
+                $regexp = sprintf('%s(?P<%s>%s)', preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]);
+                if ($index >= $firstOptional) {
+                    // Enclose each optional token in a subpattern to make it optional.
+                    // "?:" means it is non-capturing, i.e. the portion of the subject string that
+                    // matched the optional subpattern is not passed back.
+                    $regexp = "(?:$regexp";
+                    $nbTokens = count($tokens);
+                    if ($nbTokens - 1 == $index) {
+                        // Close the optional subpatterns
+                        $regexp .= str_repeat(")?", $nbTokens - $firstOptional - (0 === $firstOptional ? 1 : 0));
+                    }
+                }
+
+                return $regexp;
+            }
+        }
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/RouteCompilerInterface.php b/vendor/symfony/routing/Symfony/Component/Routing/RouteCompilerInterface.php
new file mode 100644 (file)
index 0000000..e6f8ee6
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing;
+
+/**
+ * RouteCompilerInterface is the interface that all RouteCompiler classes must implement.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface RouteCompilerInterface
+{
+    /**
+     * Compiles the current route instance.
+     *
+     * @param Route $route A Route instance
+     *
+     * @return CompiledRoute A CompiledRoute instance
+     *
+     * @throws \LogicException If the Route cannot be compiled because the
+     *                         path or host pattern is invalid
+     */
+    public static function compile(Route $route);
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Router.php b/vendor/symfony/routing/Symfony/Component/Routing/Router.php
new file mode 100644 (file)
index 0000000..d1e2897
--- /dev/null
@@ -0,0 +1,289 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing;
+
+use Symfony\Component\Config\Loader\LoaderInterface;
+use Symfony\Component\Config\ConfigCache;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\Routing\Generator\ConfigurableRequirementsInterface;
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
+use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
+
+/**
+ * The Router class is an example of the integration of all pieces of the
+ * routing system for easier use.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class Router implements RouterInterface
+{
+    /**
+     * @var UrlMatcherInterface|null
+     */
+    protected $matcher;
+
+    /**
+     * @var UrlGeneratorInterface|null
+     */
+    protected $generator;
+
+    /**
+     * @var RequestContext
+     */
+    protected $context;
+
+    /**
+     * @var LoaderInterface
+     */
+    protected $loader;
+
+    /**
+     * @var RouteCollection|null
+     */
+    protected $collection;
+
+    /**
+     * @var mixed
+     */
+    protected $resource;
+
+    /**
+     * @var array
+     */
+    protected $options = array();
+
+    /**
+     * @var LoggerInterface|null
+     */
+    protected $logger;
+
+    /**
+     * Constructor.
+     *
+     * @param LoaderInterface $loader   A LoaderInterface instance
+     * @param mixed           $resource The main resource to load
+     * @param array           $options  An array of options
+     * @param RequestContext  $context  The context
+     * @param LoggerInterface $logger   A logger instance
+     */
+    public function __construct(LoaderInterface $loader, $resource, array $options = array(), RequestContext $context = null, LoggerInterface $logger = null)
+    {
+        $this->loader = $loader;
+        $this->resource = $resource;
+        $this->logger = $logger;
+        $this->context = null === $context ? new RequestContext() : $context;
+        $this->setOptions($options);
+    }
+
+    /**
+     * Sets options.
+     *
+     * Available options:
+     *
+     *   * cache_dir:     The cache directory (or null to disable caching)
+     *   * debug:         Whether to enable debugging or not (false by default)
+     *   * resource_type: Type hint for the main resource (optional)
+     *
+     * @param array $options An array of options
+     *
+     * @throws \InvalidArgumentException When unsupported option is provided
+     */
+    public function setOptions(array $options)
+    {
+        $this->options = array(
+            'cache_dir'              => null,
+            'debug'                  => false,
+            'generator_class'        => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
+            'generator_base_class'   => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
+            'generator_dumper_class' => 'Symfony\\Component\\Routing\\Generator\\Dumper\\PhpGeneratorDumper',
+            'generator_cache_class'  => 'ProjectUrlGenerator',
+            'matcher_class'          => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
+            'matcher_base_class'     => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
+            'matcher_dumper_class'   => 'Symfony\\Component\\Routing\\Matcher\\Dumper\\PhpMatcherDumper',
+            'matcher_cache_class'    => 'ProjectUrlMatcher',
+            'resource_type'          => null,
+            'strict_requirements'    => true,
+        );
+
+        // check option names and live merge, if errors are encountered Exception will be thrown
+        $invalid = array();
+        foreach ($options as $key => $value) {
+            if (array_key_exists($key, $this->options)) {
+                $this->options[$key] = $value;
+            } else {
+                $invalid[] = $key;
+            }
+        }
+
+        if ($invalid) {
+            throw new \InvalidArgumentException(sprintf('The Router does not support the following options: "%s".', implode('", "', $invalid)));
+        }
+    }
+
+    /**
+     * Sets an option.
+     *
+     * @param string $key   The key
+     * @param mixed  $value The value
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function setOption($key, $value)
+    {
+        if (!array_key_exists($key, $this->options)) {
+            throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key));
+        }
+
+        $this->options[$key] = $value;
+    }
+
+    /**
+     * Gets an option value.
+     *
+     * @param string $key The key
+     *
+     * @return mixed The value
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function getOption($key)
+    {
+        if (!array_key_exists($key, $this->options)) {
+            throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key));
+        }
+
+        return $this->options[$key];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getRouteCollection()
+    {
+        if (null === $this->collection) {
+            $this->collection = $this->loader->load($this->resource, $this->options['resource_type']);
+        }
+
+        return $this->collection;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setContext(RequestContext $context)
+    {
+        $this->context = $context;
+
+        if (null !== $this->matcher) {
+            $this->getMatcher()->setContext($context);
+        }
+        if (null !== $this->generator) {
+            $this->getGenerator()->setContext($context);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getContext()
+    {
+        return $this->context;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH)
+    {
+        return $this->getGenerator()->generate($name, $parameters, $referenceType);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function match($pathinfo)
+    {
+        return $this->getMatcher()->match($pathinfo);
+    }
+
+    /**
+     * Gets the UrlMatcher instance associated with this Router.
+     *
+     * @return UrlMatcherInterface A UrlMatcherInterface instance
+     */
+    public function getMatcher()
+    {
+        if (null !== $this->matcher) {
+            return $this->matcher;
+        }
+
+        if (null === $this->options['cache_dir'] || null === $this->options['matcher_cache_class']) {
+            return $this->matcher = new $this->options['matcher_class']($this->getRouteCollection(), $this->context);
+        }
+
+        $class = $this->options['matcher_cache_class'];
+        $cache = new ConfigCache($this->options['cache_dir'].'/'.$class.'.php', $this->options['debug']);
+        if (!$cache->isFresh($class)) {
+            $dumper = new $this->options['matcher_dumper_class']($this->getRouteCollection());
+
+            $options = array(
+                'class'      => $class,
+                'base_class' => $this->options['matcher_base_class'],
+            );
+
+            $cache->write($dumper->dump($options), $this->getRouteCollection()->getResources());
+        }
+
+        require_once $cache;
+
+        return $this->matcher = new $class($this->context);
+    }
+
+    /**
+     * Gets the UrlGenerator instance associated with this Router.
+     *
+     * @return UrlGeneratorInterface A UrlGeneratorInterface instance
+     */
+    public function getGenerator()
+    {
+        if (null !== $this->generator) {
+            return $this->generator;
+        }
+
+        if (null === $this->options['cache_dir'] || null === $this->options['generator_cache_class']) {
+            $this->generator = new $this->options['generator_class']($this->getRouteCollection(), $this->context, $this->logger);
+        } else {
+            $class = $this->options['generator_cache_class'];
+            $cache = new ConfigCache($this->options['cache_dir'].'/'.$class.'.php', $this->options['debug']);
+            if (!$cache->isFresh($class)) {
+                $dumper = new $this->options['generator_dumper_class']($this->getRouteCollection());
+
+                $options = array(
+                    'class'      => $class,
+                    'base_class' => $this->options['generator_base_class'],
+                );
+
+                $cache->write($dumper->dump($options), $this->getRouteCollection()->getResources());
+            }
+
+            require_once $cache;
+
+            $this->generator = new $class($this->context, $this->logger);
+        }
+
+        if ($this->generator instanceof ConfigurableRequirementsInterface) {
+            $this->generator->setStrictRequirements($this->options['strict_requirements']);
+        }
+
+        return $this->generator;
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/RouterInterface.php b/vendor/symfony/routing/Symfony/Component/Routing/RouterInterface.php
new file mode 100644 (file)
index 0000000..a10ae34
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing;
+
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
+use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
+
+/**
+ * RouterInterface is the interface that all Router classes must implement.
+ *
+ * This interface is the concatenation of UrlMatcherInterface and UrlGeneratorInterface.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface RouterInterface extends UrlMatcherInterface, UrlGeneratorInterface
+{
+    /**
+     * Gets the RouteCollection instance associated with this Router.
+     *
+     * @return RouteCollection A RouteCollection instance
+     */
+    public function getRouteCollection();
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Annotation/RouteTest.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Annotation/RouteTest.php
new file mode 100644 (file)
index 0000000..b58869f
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Annotation;
+
+use Symfony\Component\Routing\Annotation\Route;
+
+class RouteTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @expectedException \BadMethodCallException
+     */
+    public function testInvalidRouteParameter()
+    {
+        $route = new Route(array('foo' => 'bar'));
+    }
+
+    /**
+     * @dataProvider getValidParameters
+     */
+    public function testRouteParameters($parameter, $value, $getter)
+    {
+        $route = new Route(array($parameter => $value));
+        $this->assertEquals($route->$getter(), $value);
+    }
+
+    public function getValidParameters()
+    {
+        return array(
+           array('value', '/Blog', 'getPattern'),
+           array('value', '/Blog', 'getPath'),
+           array('requirements', array('_method' => 'GET'), 'getRequirements'),
+           array('options', array('compiler_class' => 'RouteCompiler'), 'getOptions'),
+           array('name', 'blog_index', 'getName'),
+           array('defaults', array('_controller' => 'MyBlogBundle:Blog:index'), 'getDefaults'),
+           array('schemes', array('https'), 'getSchemes'),
+           array('methods', array('GET', 'POST'), 'getMethods'),
+           array('host', array('{locale}.example.com'), 'getHost')
+        );
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/CompiledRouteTest.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/CompiledRouteTest.php
new file mode 100644 (file)
index 0000000..215ebb7
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests;
+
+use Symfony\Component\Routing\CompiledRoute;
+
+class CompiledRouteTest extends \PHPUnit_Framework_TestCase
+{
+    public function testAccessors()
+    {
+        $compiled = new CompiledRoute('prefix', 'regex', array('tokens'), array(), array(), array(), array(), array('variables'));
+        $this->assertEquals('prefix', $compiled->getStaticPrefix(), '__construct() takes a static prefix as its second argument');
+        $this->assertEquals('regex', $compiled->getRegex(), '__construct() takes a regexp as its third argument');
+        $this->assertEquals(array('tokens'), $compiled->getTokens(), '__construct() takes an array of tokens as its fourth argument');
+        $this->assertEquals(array('variables'), $compiled->getVariables(), '__construct() takes an array of variables as its ninth argument');
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/AbstractClass.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/AbstractClass.php
new file mode 100644 (file)
index 0000000..56bcab2
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses;
+
+abstract class AbstractClass
+{
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/BarClass.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/BarClass.php
new file mode 100644 (file)
index 0000000..a388277
--- /dev/null
@@ -0,0 +1,19 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses;
+
+class BarClass
+{
+    public function routeAction($arg1, $arg2 = 'defaultValue2', $arg3 = 'defaultValue3')
+    {
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/FooClass.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/FooClass.php
new file mode 100644 (file)
index 0000000..320dc35
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses;
+
+class FooClass
+{
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/CustomXmlFileLoader.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/CustomXmlFileLoader.php
new file mode 100644 (file)
index 0000000..12a5bed
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Fixtures;
+
+use Symfony\Component\Routing\Loader\XmlFileLoader;
+use Symfony\Component\Config\Util\XmlUtils;
+
+/**
+ * XmlFileLoader with schema validation turned off
+ */
+class CustomXmlFileLoader extends XmlFileLoader
+{
+    protected function loadFile($file)
+    {
+        return XmlUtils::loadFile($file, function() { return true; });
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/RedirectableUrlMatcher.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/RedirectableUrlMatcher.php
new file mode 100644 (file)
index 0000000..e95c1f2
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Fixtures;
+
+use Symfony\Component\Routing\Matcher\UrlMatcher;
+use Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface;
+
+/**
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class RedirectableUrlMatcher extends UrlMatcher implements RedirectableUrlMatcherInterface
+{
+    public function redirect($path, $route, $scheme = null)
+    {
+        return array(
+            '_controller' => 'Some controller reference...',
+            'path'        => $path,
+            'scheme'      => $scheme,
+        );
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/annotated.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/annotated.php
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.apache b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.apache
new file mode 100644 (file)
index 0000000..26a561c
--- /dev/null
@@ -0,0 +1,163 @@
+# skip "real" requests
+RewriteCond %{REQUEST_FILENAME} -f
+RewriteRule .* - [QSA,L]
+
+# foo
+RewriteCond %{REQUEST_URI} ^/foo/(baz|symfony)$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:foo,E=_ROUTING_param_bar:%1,E=_ROUTING_default_def:test]
+
+# foobar
+RewriteCond %{REQUEST_URI} ^/foo(?:/([^/]++))?$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:foobar,E=_ROUTING_param_bar:%1,E=_ROUTING_default_bar:toto]
+
+# bar
+RewriteCond %{REQUEST_URI} ^/bar/([^/]++)$
+RewriteCond %{REQUEST_METHOD} !^(GET|HEAD)$ [NC]
+RewriteRule .* - [S=1,E=_ROUTING_allow_GET:1,E=_ROUTING_allow_HEAD:1]
+RewriteCond %{REQUEST_URI} ^/bar/([^/]++)$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:bar,E=_ROUTING_param_foo:%1]
+
+# baragain
+RewriteCond %{REQUEST_URI} ^/baragain/([^/]++)$
+RewriteCond %{REQUEST_METHOD} !^(GET|POST|HEAD)$ [NC]
+RewriteRule .* - [S=1,E=_ROUTING_allow_GET:1,E=_ROUTING_allow_POST:1,E=_ROUTING_allow_HEAD:1]
+RewriteCond %{REQUEST_URI} ^/baragain/([^/]++)$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baragain,E=_ROUTING_param_foo:%1]
+
+# baz
+RewriteCond %{REQUEST_URI} ^/test/baz$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz]
+
+# baz2
+RewriteCond %{REQUEST_URI} ^/test/baz\.html$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz2]
+
+# baz3
+RewriteCond %{REQUEST_URI} ^/test/baz3$
+RewriteRule .* $0/ [QSA,L,R=301]
+RewriteCond %{REQUEST_URI} ^/test/baz3/$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz3]
+
+# baz4
+RewriteCond %{REQUEST_URI} ^/test/([^/]++)$
+RewriteRule .* $0/ [QSA,L,R=301]
+RewriteCond %{REQUEST_URI} ^/test/([^/]++)/$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz4,E=_ROUTING_param_foo:%1]
+
+# baz5
+RewriteCond %{REQUEST_URI} ^/test/([^/]++)/$
+RewriteCond %{REQUEST_METHOD} !^(GET|HEAD)$ [NC]
+RewriteRule .* - [S=2,E=_ROUTING_allow_GET:1,E=_ROUTING_allow_HEAD:1]
+RewriteCond %{REQUEST_URI} ^/test/([^/]++)$
+RewriteRule .* $0/ [QSA,L,R=301]
+RewriteCond %{REQUEST_URI} ^/test/([^/]++)/$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz5,E=_ROUTING_param_foo:%1]
+
+# baz5unsafe
+RewriteCond %{REQUEST_URI} ^/testunsafe/([^/]++)/$
+RewriteCond %{REQUEST_METHOD} !^(POST)$ [NC]
+RewriteRule .* - [S=1,E=_ROUTING_allow_POST:1]
+RewriteCond %{REQUEST_URI} ^/testunsafe/([^/]++)/$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz5unsafe,E=_ROUTING_param_foo:%1]
+
+# baz6
+RewriteCond %{REQUEST_URI} ^/test/baz$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz6,E=_ROUTING_default_foo:bar\ baz]
+
+# baz7
+RewriteCond %{REQUEST_URI} ^/te\ st/baz$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz7]
+
+# baz8
+RewriteCond %{REQUEST_URI} ^/te\\\ st/baz$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz8]
+
+# baz9
+RewriteCond %{REQUEST_URI} ^/test/(te\\\ st)$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz9,E=_ROUTING_param_baz:%1]
+
+RewriteCond %{HTTP:Host} ^a\.example\.com$
+RewriteRule .? - [E=__ROUTING_host_1:1]
+
+# route1
+RewriteCond %{ENV:__ROUTING_host_1} =1
+RewriteCond %{REQUEST_URI} ^/route1$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route1]
+
+# route2
+RewriteCond %{ENV:__ROUTING_host_1} =1
+RewriteCond %{REQUEST_URI} ^/c2/route2$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route2]
+
+RewriteCond %{HTTP:Host} ^b\.example\.com$
+RewriteRule .? - [E=__ROUTING_host_2:1]
+
+# route3
+RewriteCond %{ENV:__ROUTING_host_2} =1
+RewriteCond %{REQUEST_URI} ^/c2/route3$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route3]
+
+RewriteCond %{HTTP:Host} ^a\.example\.com$
+RewriteRule .? - [E=__ROUTING_host_3:1]
+
+# route4
+RewriteCond %{ENV:__ROUTING_host_3} =1
+RewriteCond %{REQUEST_URI} ^/route4$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route4]
+
+RewriteCond %{HTTP:Host} ^c\.example\.com$
+RewriteRule .? - [E=__ROUTING_host_4:1]
+
+# route5
+RewriteCond %{ENV:__ROUTING_host_4} =1
+RewriteCond %{REQUEST_URI} ^/route5$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route5]
+
+# route6
+RewriteCond %{REQUEST_URI} ^/route6$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route6]
+
+RewriteCond %{HTTP:Host} ^([^\.]++)\.example\.com$
+RewriteRule .? - [E=__ROUTING_host_5:1,E=__ROUTING_host_5_var1:%1]
+
+# route11
+RewriteCond %{ENV:__ROUTING_host_5} =1
+RewriteCond %{REQUEST_URI} ^/route11$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route11,E=_ROUTING_param_var1:%{ENV:__ROUTING_host_5_var1}]
+
+# route12
+RewriteCond %{ENV:__ROUTING_host_5} =1
+RewriteCond %{REQUEST_URI} ^/route12$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route12,E=_ROUTING_param_var1:%{ENV:__ROUTING_host_5_var1},E=_ROUTING_default_var1:val]
+
+# route13
+RewriteCond %{ENV:__ROUTING_host_5} =1
+RewriteCond %{REQUEST_URI} ^/route13/([^/]++)$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route13,E=_ROUTING_param_var1:%{ENV:__ROUTING_host_5_var1},E=_ROUTING_param_name:%1]
+
+# route14
+RewriteCond %{ENV:__ROUTING_host_5} =1
+RewriteCond %{REQUEST_URI} ^/route14/([^/]++)$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route14,E=_ROUTING_param_var1:%{ENV:__ROUTING_host_5_var1},E=_ROUTING_param_name:%1,E=_ROUTING_default_var1:val]
+
+RewriteCond %{HTTP:Host} ^c\.example\.com$
+RewriteRule .? - [E=__ROUTING_host_6:1]
+
+# route15
+RewriteCond %{ENV:__ROUTING_host_6} =1
+RewriteCond %{REQUEST_URI} ^/route15/([^/]++)$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route15,E=_ROUTING_param_name:%1]
+
+# route16
+RewriteCond %{REQUEST_URI} ^/route16/([^/]++)$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route16,E=_ROUTING_param_name:%1,E=_ROUTING_default_var1:val]
+
+# route17
+RewriteCond %{REQUEST_URI} ^/route17$
+RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route17]
+
+# 405 Method Not Allowed
+RewriteCond %{ENV:_ROUTING__allow_GET} =1 [OR]
+RewriteCond %{ENV:_ROUTING__allow_HEAD} =1 [OR]
+RewriteCond %{ENV:_ROUTING__allow_POST} =1
+RewriteRule .* app.php [QSA,L]
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php
new file mode 100644 (file)
index 0000000..e5f7665
--- /dev/null
@@ -0,0 +1,310 @@
+<?php
+
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\RequestContext;
+
+/**
+ * ProjectUrlMatcher
+ *
+ * This class has been auto-generated
+ * by the Symfony Routing Component.
+ */
+class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
+{
+    /**
+     * Constructor.
+     */
+    public function __construct(RequestContext $context)
+    {
+        $this->context = $context;
+    }
+
+    public function match($pathinfo)
+    {
+        $allow = array();
+        $pathinfo = rawurldecode($pathinfo);
+
+        // foo
+        if (0 === strpos($pathinfo, '/foo') && preg_match('#^/foo/(?P<bar>baz|symfony)$#s', $pathinfo, $matches)) {
+            return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo')), array (  'def' => 'test',));
+        }
+
+        if (0 === strpos($pathinfo, '/bar')) {
+            // bar
+            if (preg_match('#^/bar/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
+                if (!in_array($this->context->getMethod(), array('GET', 'HEAD'))) {
+                    $allow = array_merge($allow, array('GET', 'HEAD'));
+                    goto not_bar;
+                }
+
+                return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar')), array ());
+            }
+            not_bar:
+
+            // barhead
+            if (0 === strpos($pathinfo, '/barhead') && preg_match('#^/barhead/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
+                if (!in_array($this->context->getMethod(), array('GET', 'HEAD'))) {
+                    $allow = array_merge($allow, array('GET', 'HEAD'));
+                    goto not_barhead;
+                }
+
+                return $this->mergeDefaults(array_replace($matches, array('_route' => 'barhead')), array ());
+            }
+            not_barhead:
+
+        }
+
+        if (0 === strpos($pathinfo, '/test')) {
+            if (0 === strpos($pathinfo, '/test/baz')) {
+                // baz
+                if ($pathinfo === '/test/baz') {
+                    return array('_route' => 'baz');
+                }
+
+                // baz2
+                if ($pathinfo === '/test/baz.html') {
+                    return array('_route' => 'baz2');
+                }
+
+                // baz3
+                if ($pathinfo === '/test/baz3/') {
+                    return array('_route' => 'baz3');
+                }
+
+            }
+
+            // baz4
+            if (preg_match('#^/test/(?P<foo>[^/]++)/$#s', $pathinfo, $matches)) {
+                return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz4')), array ());
+            }
+
+            // baz5
+            if (preg_match('#^/test/(?P<foo>[^/]++)/$#s', $pathinfo, $matches)) {
+                if ($this->context->getMethod() != 'POST') {
+                    $allow[] = 'POST';
+                    goto not_baz5;
+                }
+
+                return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz5')), array ());
+            }
+            not_baz5:
+
+            // baz.baz6
+            if (preg_match('#^/test/(?P<foo>[^/]++)/$#s', $pathinfo, $matches)) {
+                if ($this->context->getMethod() != 'PUT') {
+                    $allow[] = 'PUT';
+                    goto not_bazbaz6;
+                }
+
+                return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz.baz6')), array ());
+            }
+            not_bazbaz6:
+
+        }
+
+        // foofoo
+        if ($pathinfo === '/foofoo') {
+            return array (  'def' => 'test',  '_route' => 'foofoo',);
+        }
+
+        // quoter
+        if (preg_match('#^/(?P<quoter>[\']+)$#s', $pathinfo, $matches)) {
+            return $this->mergeDefaults(array_replace($matches, array('_route' => 'quoter')), array ());
+        }
+
+        // space
+        if ($pathinfo === '/spa ce') {
+            return array('_route' => 'space');
+        }
+
+        if (0 === strpos($pathinfo, '/a')) {
+            if (0 === strpos($pathinfo, '/a/b\'b')) {
+                // foo1
+                if (preg_match('#^/a/b\'b/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
+                    return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo1')), array ());
+                }
+
+                // bar1
+                if (preg_match('#^/a/b\'b/(?P<bar>[^/]++)$#s', $pathinfo, $matches)) {
+                    return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar1')), array ());
+                }
+
+            }
+
+            // overridden
+            if (preg_match('#^/a/(?P<var>.*)$#s', $pathinfo, $matches)) {
+                return $this->mergeDefaults(array_replace($matches, array('_route' => 'overridden')), array ());
+            }
+
+            if (0 === strpos($pathinfo, '/a/b\'b')) {
+                // foo2
+                if (preg_match('#^/a/b\'b/(?P<foo1>[^/]++)$#s', $pathinfo, $matches)) {
+                    return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo2')), array ());
+                }
+
+                // bar2
+                if (preg_match('#^/a/b\'b/(?P<bar1>[^/]++)$#s', $pathinfo, $matches)) {
+                    return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar2')), array ());
+                }
+
+            }
+
+        }
+
+        if (0 === strpos($pathinfo, '/multi')) {
+            // helloWorld
+            if (0 === strpos($pathinfo, '/multi/hello') && preg_match('#^/multi/hello(?:/(?P<who>[^/]++))?$#s', $pathinfo, $matches)) {
+                return $this->mergeDefaults(array_replace($matches, array('_route' => 'helloWorld')), array (  'who' => 'World!',));
+            }
+
+            // overridden2
+            if ($pathinfo === '/multi/new') {
+                return array('_route' => 'overridden2');
+            }
+
+            // hey
+            if ($pathinfo === '/multi/hey/') {
+                return array('_route' => 'hey');
+            }
+
+        }
+
+        // foo3
+        if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
+            return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo3')), array ());
+        }
+
+        // bar3
+        if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P<bar>[^/]++)$#s', $pathinfo, $matches)) {
+            return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar3')), array ());
+        }
+
+        if (0 === strpos($pathinfo, '/aba')) {
+            // ababa
+            if ($pathinfo === '/ababa') {
+                return array('_route' => 'ababa');
+            }
+
+            // foo4
+            if (preg_match('#^/aba/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
+                return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo4')), array ());
+            }
+
+        }
+
+        $host = $this->context->getHost();
+
+        if (preg_match('#^a\\.example\\.com$#s', $host, $hostMatches)) {
+            // route1
+            if ($pathinfo === '/route1') {
+                return array('_route' => 'route1');
+            }
+
+            // route2
+            if ($pathinfo === '/c2/route2') {
+                return array('_route' => 'route2');
+            }
+
+        }
+
+        if (preg_match('#^b\\.example\\.com$#s', $host, $hostMatches)) {
+            // route3
+            if ($pathinfo === '/c2/route3') {
+                return array('_route' => 'route3');
+            }
+
+        }
+
+        if (preg_match('#^a\\.example\\.com$#s', $host, $hostMatches)) {
+            // route4
+            if ($pathinfo === '/route4') {
+                return array('_route' => 'route4');
+            }
+
+        }
+
+        if (preg_match('#^c\\.example\\.com$#s', $host, $hostMatches)) {
+            // route5
+            if ($pathinfo === '/route5') {
+                return array('_route' => 'route5');
+            }
+
+        }
+
+        // route6
+        if ($pathinfo === '/route6') {
+            return array('_route' => 'route6');
+        }
+
+        if (preg_match('#^(?P<var1>[^\\.]++)\\.example\\.com$#s', $host, $hostMatches)) {
+            if (0 === strpos($pathinfo, '/route1')) {
+                // route11
+                if ($pathinfo === '/route11') {
+                    return $this->mergeDefaults(array_replace($hostMatches, array('_route' => 'route11')), array ());
+                }
+
+                // route12
+                if ($pathinfo === '/route12') {
+                    return $this->mergeDefaults(array_replace($hostMatches, array('_route' => 'route12')), array (  'var1' => 'val',));
+                }
+
+                // route13
+                if (0 === strpos($pathinfo, '/route13') && preg_match('#^/route13/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
+                    return $this->mergeDefaults(array_replace($hostMatches, $matches, array('_route' => 'route13')), array ());
+                }
+
+                // route14
+                if (0 === strpos($pathinfo, '/route14') && preg_match('#^/route14/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
+                    return $this->mergeDefaults(array_replace($hostMatches, $matches, array('_route' => 'route14')), array (  'var1' => 'val',));
+                }
+
+            }
+
+        }
+
+        if (preg_match('#^c\\.example\\.com$#s', $host, $hostMatches)) {
+            // route15
+            if (0 === strpos($pathinfo, '/route15') && preg_match('#^/route15/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
+                return $this->mergeDefaults(array_replace($matches, array('_route' => 'route15')), array ());
+            }
+
+        }
+
+        if (0 === strpos($pathinfo, '/route1')) {
+            // route16
+            if (0 === strpos($pathinfo, '/route16') && preg_match('#^/route16/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
+                return $this->mergeDefaults(array_replace($matches, array('_route' => 'route16')), array (  'var1' => 'val',));
+            }
+
+            // route17
+            if ($pathinfo === '/route17') {
+                return array('_route' => 'route17');
+            }
+
+        }
+
+        if (0 === strpos($pathinfo, '/a')) {
+            // a
+            if ($pathinfo === '/a/a...') {
+                return array('_route' => 'a');
+            }
+
+            if (0 === strpos($pathinfo, '/a/b')) {
+                // b
+                if (preg_match('#^/a/b/(?P<var>[^/]++)$#s', $pathinfo, $matches)) {
+                    return $this->mergeDefaults(array_replace($matches, array('_route' => 'b')), array ());
+                }
+
+                // c
+                if (0 === strpos($pathinfo, '/a/b/c') && preg_match('#^/a/b/c/(?P<var>[^/]++)$#s', $pathinfo, $matches)) {
+                    return $this->mergeDefaults(array_replace($matches, array('_route' => 'c')), array ());
+                }
+
+            }
+
+        }
+
+        throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException();
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.apache b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.apache
new file mode 100644 (file)
index 0000000..309f2ff
--- /dev/null
@@ -0,0 +1,7 @@
+# skip "real" requests
+RewriteCond %{REQUEST_FILENAME} -f
+RewriteRule .* - [QSA,L]
+
+# foo
+RewriteCond %{REQUEST_URI} ^/foo$
+RewriteRule .* ap\ p_d\ ev.php [QSA,L,E=_ROUTING_route:foo]
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php
new file mode 100644 (file)
index 0000000..ad15790
--- /dev/null
@@ -0,0 +1,340 @@
+<?php
+
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\RequestContext;
+
+/**
+ * ProjectUrlMatcher
+ *
+ * This class has been auto-generated
+ * by the Symfony Routing Component.
+ */
+class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\RedirectableUrlMatcher
+{
+    /**
+     * Constructor.
+     */
+    public function __construct(RequestContext $context)
+    {
+        $this->context = $context;
+    }
+
+    public function match($pathinfo)
+    {
+        $allow = array();
+        $pathinfo = rawurldecode($pathinfo);
+
+        // foo
+        if (0 === strpos($pathinfo, '/foo') && preg_match('#^/foo/(?P<bar>baz|symfony)$#s', $pathinfo, $matches)) {
+            return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo')), array (  'def' => 'test',));
+        }
+
+        if (0 === strpos($pathinfo, '/bar')) {
+            // bar
+            if (preg_match('#^/bar/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
+                if (!in_array($this->context->getMethod(), array('GET', 'HEAD'))) {
+                    $allow = array_merge($allow, array('GET', 'HEAD'));
+                    goto not_bar;
+                }
+
+                return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar')), array ());
+            }
+            not_bar:
+
+            // barhead
+            if (0 === strpos($pathinfo, '/barhead') && preg_match('#^/barhead/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
+                if (!in_array($this->context->getMethod(), array('GET', 'HEAD'))) {
+                    $allow = array_merge($allow, array('GET', 'HEAD'));
+                    goto not_barhead;
+                }
+
+                return $this->mergeDefaults(array_replace($matches, array('_route' => 'barhead')), array ());
+            }
+            not_barhead:
+
+        }
+
+        if (0 === strpos($pathinfo, '/test')) {
+            if (0 === strpos($pathinfo, '/test/baz')) {
+                // baz
+                if ($pathinfo === '/test/baz') {
+                    return array('_route' => 'baz');
+                }
+
+                // baz2
+                if ($pathinfo === '/test/baz.html') {
+                    return array('_route' => 'baz2');
+                }
+
+                // baz3
+                if (rtrim($pathinfo, '/') === '/test/baz3') {
+                    if (substr($pathinfo, -1) !== '/') {
+                        return $this->redirect($pathinfo.'/', 'baz3');
+                    }
+
+                    return array('_route' => 'baz3');
+                }
+
+            }
+
+            // baz4
+            if (preg_match('#^/test/(?P<foo>[^/]++)/?$#s', $pathinfo, $matches)) {
+                if (substr($pathinfo, -1) !== '/') {
+                    return $this->redirect($pathinfo.'/', 'baz4');
+                }
+
+                return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz4')), array ());
+            }
+
+            // baz5
+            if (preg_match('#^/test/(?P<foo>[^/]++)/$#s', $pathinfo, $matches)) {
+                if ($this->context->getMethod() != 'POST') {
+                    $allow[] = 'POST';
+                    goto not_baz5;
+                }
+
+                return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz5')), array ());
+            }
+            not_baz5:
+
+            // baz.baz6
+            if (preg_match('#^/test/(?P<foo>[^/]++)/$#s', $pathinfo, $matches)) {
+                if ($this->context->getMethod() != 'PUT') {
+                    $allow[] = 'PUT';
+                    goto not_bazbaz6;
+                }
+
+                return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz.baz6')), array ());
+            }
+            not_bazbaz6:
+
+        }
+
+        // foofoo
+        if ($pathinfo === '/foofoo') {
+            return array (  'def' => 'test',  '_route' => 'foofoo',);
+        }
+
+        // quoter
+        if (preg_match('#^/(?P<quoter>[\']+)$#s', $pathinfo, $matches)) {
+            return $this->mergeDefaults(array_replace($matches, array('_route' => 'quoter')), array ());
+        }
+
+        // space
+        if ($pathinfo === '/spa ce') {
+            return array('_route' => 'space');
+        }
+
+        if (0 === strpos($pathinfo, '/a')) {
+            if (0 === strpos($pathinfo, '/a/b\'b')) {
+                // foo1
+                if (preg_match('#^/a/b\'b/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
+                    return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo1')), array ());
+                }
+
+                // bar1
+                if (preg_match('#^/a/b\'b/(?P<bar>[^/]++)$#s', $pathinfo, $matches)) {
+                    return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar1')), array ());
+                }
+
+            }
+
+            // overridden
+            if (preg_match('#^/a/(?P<var>.*)$#s', $pathinfo, $matches)) {
+                return $this->mergeDefaults(array_replace($matches, array('_route' => 'overridden')), array ());
+            }
+
+            if (0 === strpos($pathinfo, '/a/b\'b')) {
+                // foo2
+                if (preg_match('#^/a/b\'b/(?P<foo1>[^/]++)$#s', $pathinfo, $matches)) {
+                    return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo2')), array ());
+                }
+
+                // bar2
+                if (preg_match('#^/a/b\'b/(?P<bar1>[^/]++)$#s', $pathinfo, $matches)) {
+                    return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar2')), array ());
+                }
+
+            }
+
+        }
+
+        if (0 === strpos($pathinfo, '/multi')) {
+            // helloWorld
+            if (0 === strpos($pathinfo, '/multi/hello') && preg_match('#^/multi/hello(?:/(?P<who>[^/]++))?$#s', $pathinfo, $matches)) {
+                return $this->mergeDefaults(array_replace($matches, array('_route' => 'helloWorld')), array (  'who' => 'World!',));
+            }
+
+            // overridden2
+            if ($pathinfo === '/multi/new') {
+                return array('_route' => 'overridden2');
+            }
+
+            // hey
+            if (rtrim($pathinfo, '/') === '/multi/hey') {
+                if (substr($pathinfo, -1) !== '/') {
+                    return $this->redirect($pathinfo.'/', 'hey');
+                }
+
+                return array('_route' => 'hey');
+            }
+
+        }
+
+        // foo3
+        if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
+            return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo3')), array ());
+        }
+
+        // bar3
+        if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P<bar>[^/]++)$#s', $pathinfo, $matches)) {
+            return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar3')), array ());
+        }
+
+        if (0 === strpos($pathinfo, '/aba')) {
+            // ababa
+            if ($pathinfo === '/ababa') {
+                return array('_route' => 'ababa');
+            }
+
+            // foo4
+            if (preg_match('#^/aba/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
+                return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo4')), array ());
+            }
+
+        }
+
+        $host = $this->context->getHost();
+
+        if (preg_match('#^a\\.example\\.com$#s', $host, $hostMatches)) {
+            // route1
+            if ($pathinfo === '/route1') {
+                return array('_route' => 'route1');
+            }
+
+            // route2
+            if ($pathinfo === '/c2/route2') {
+                return array('_route' => 'route2');
+            }
+
+        }
+
+        if (preg_match('#^b\\.example\\.com$#s', $host, $hostMatches)) {
+            // route3
+            if ($pathinfo === '/c2/route3') {
+                return array('_route' => 'route3');
+            }
+
+        }
+
+        if (preg_match('#^a\\.example\\.com$#s', $host, $hostMatches)) {
+            // route4
+            if ($pathinfo === '/route4') {
+                return array('_route' => 'route4');
+            }
+
+        }
+
+        if (preg_match('#^c\\.example\\.com$#s', $host, $hostMatches)) {
+            // route5
+            if ($pathinfo === '/route5') {
+                return array('_route' => 'route5');
+            }
+
+        }
+
+        // route6
+        if ($pathinfo === '/route6') {
+            return array('_route' => 'route6');
+        }
+
+        if (preg_match('#^(?P<var1>[^\\.]++)\\.example\\.com$#s', $host, $hostMatches)) {
+            if (0 === strpos($pathinfo, '/route1')) {
+                // route11
+                if ($pathinfo === '/route11') {
+                    return $this->mergeDefaults(array_replace($hostMatches, array('_route' => 'route11')), array ());
+                }
+
+                // route12
+                if ($pathinfo === '/route12') {
+                    return $this->mergeDefaults(array_replace($hostMatches, array('_route' => 'route12')), array (  'var1' => 'val',));
+                }
+
+                // route13
+                if (0 === strpos($pathinfo, '/route13') && preg_match('#^/route13/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
+                    return $this->mergeDefaults(array_replace($hostMatches, $matches, array('_route' => 'route13')), array ());
+                }
+
+                // route14
+                if (0 === strpos($pathinfo, '/route14') && preg_match('#^/route14/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
+                    return $this->mergeDefaults(array_replace($hostMatches, $matches, array('_route' => 'route14')), array (  'var1' => 'val',));
+                }
+
+            }
+
+        }
+
+        if (preg_match('#^c\\.example\\.com$#s', $host, $hostMatches)) {
+            // route15
+            if (0 === strpos($pathinfo, '/route15') && preg_match('#^/route15/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
+                return $this->mergeDefaults(array_replace($matches, array('_route' => 'route15')), array ());
+            }
+
+        }
+
+        if (0 === strpos($pathinfo, '/route1')) {
+            // route16
+            if (0 === strpos($pathinfo, '/route16') && preg_match('#^/route16/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
+                return $this->mergeDefaults(array_replace($matches, array('_route' => 'route16')), array (  'var1' => 'val',));
+            }
+
+            // route17
+            if ($pathinfo === '/route17') {
+                return array('_route' => 'route17');
+            }
+
+        }
+
+        if (0 === strpos($pathinfo, '/a')) {
+            // a
+            if ($pathinfo === '/a/a...') {
+                return array('_route' => 'a');
+            }
+
+            if (0 === strpos($pathinfo, '/a/b')) {
+                // b
+                if (preg_match('#^/a/b/(?P<var>[^/]++)$#s', $pathinfo, $matches)) {
+                    return $this->mergeDefaults(array_replace($matches, array('_route' => 'b')), array ());
+                }
+
+                // c
+                if (0 === strpos($pathinfo, '/a/b/c') && preg_match('#^/a/b/c/(?P<var>[^/]++)$#s', $pathinfo, $matches)) {
+                    return $this->mergeDefaults(array_replace($matches, array('_route' => 'c')), array ());
+                }
+
+            }
+
+        }
+
+        // secure
+        if ($pathinfo === '/secure') {
+            if ($this->context->getScheme() !== 'https') {
+                return $this->redirect($pathinfo, 'secure', 'https');
+            }
+
+            return array('_route' => 'secure');
+        }
+
+        // nonsecure
+        if ($pathinfo === '/nonsecure') {
+            if ($this->context->getScheme() !== 'http') {
+                return $this->redirect($pathinfo, 'nonsecure', 'http');
+            }
+
+            return array('_route' => 'nonsecure');
+        }
+
+        throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException();
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php
new file mode 100644 (file)
index 0000000..f2f642e
--- /dev/null
@@ -0,0 +1,43 @@
+<?php
+
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\RequestContext;
+
+/**
+ * ProjectUrlMatcher
+ *
+ * This class has been auto-generated
+ * by the Symfony Routing Component.
+ */
+class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
+{
+    /**
+     * Constructor.
+     */
+    public function __construct(RequestContext $context)
+    {
+        $this->context = $context;
+    }
+
+    public function match($pathinfo)
+    {
+        $allow = array();
+        $pathinfo = rawurldecode($pathinfo);
+
+        if (0 === strpos($pathinfo, '/rootprefix')) {
+            // static
+            if ($pathinfo === '/rootprefix/test') {
+                return array('_route' => 'static');
+            }
+
+            // dynamic
+            if (preg_match('#^/rootprefix/(?P<var>[^/]++)$#s', $pathinfo, $matches)) {
+                return $this->mergeDefaults(array_replace($matches, array('_route' => 'dynamic')), array ());
+            }
+
+        }
+
+        throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException();
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/empty.yml b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/empty.yml
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/foo.xml b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/foo.xml
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/foo1.xml b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/foo1.xml
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/incomplete.yml b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/incomplete.yml
new file mode 100644 (file)
index 0000000..df64d32
--- /dev/null
@@ -0,0 +1,2 @@
+blog_show:
+    defaults:  { _controller: MyBlogBundle:Blog:show }
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/missing_id.xml b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/missing_id.xml
new file mode 100644 (file)
index 0000000..4ea4115
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<routes xmlns="http://symfony.com/schema/routing"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
+
+    <route path="/test"></route>
+</routes>
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/missing_path.xml b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/missing_path.xml
new file mode 100644 (file)
index 0000000..ef5bc08
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<routes xmlns="http://symfony.com/schema/routing"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
+
+    <route id="myroute"></route>
+</routes>
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/namespaceprefix.xml b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/namespaceprefix.xml
new file mode 100644 (file)
index 0000000..bdd6a47
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<r:routes xmlns:r="http://symfony.com/schema/routing"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
+
+    <r:route id="blog_show" path="/blog/{slug}" host="{_locale}.example.com">
+        <r:default key="_controller">MyBundle:Blog:show</r:default>
+        <requirement xmlns="http://symfony.com/schema/routing" key="slug">\w+</requirement>
+        <r2:requirement xmlns:r2="http://symfony.com/schema/routing" key="_locale">en|fr|de</r2:requirement>
+        <r:option key="compiler_class">RouteCompiler</r:option>
+    </r:route>
+</r:routes>
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonesense_resource_plus_path.yml b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonesense_resource_plus_path.yml
new file mode 100644 (file)
index 0000000..a3e9473
--- /dev/null
@@ -0,0 +1,3 @@
+blog_show:
+    resource: validpattern.yml
+    path:     /test
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonesense_type_without_resource.yml b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonesense_type_without_resource.yml
new file mode 100644 (file)
index 0000000..547cda3
--- /dev/null
@@ -0,0 +1,3 @@
+blog_show:
+    path:    /blog/{slug}
+    type:    custom
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalid.xml b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalid.xml
new file mode 100644 (file)
index 0000000..755e443
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<routes xmlns="http://symfony.com/schema/routing"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
+
+    <route id="blog_show" path="/blog/{slug}">
+        <default key="_controller">MyBundle:Blog:show</default>
+        <requirement key="_method">GET</requirement>
+    <!-- </route> -->
+</routes>
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalid.yml b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalid.yml
new file mode 100644 (file)
index 0000000..257cc56
--- /dev/null
@@ -0,0 +1 @@
+foo
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalid2.yml b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalid2.yml
new file mode 100644 (file)
index 0000000..cfa9992
--- /dev/null
@@ -0,0 +1 @@
+route: string
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalidkeys.yml b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalidkeys.yml
new file mode 100644 (file)
index 0000000..015e270
--- /dev/null
@@ -0,0 +1,3 @@
+someroute:
+  resource: path/to/some.yml
+  name_prefix: test_
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalidnode.xml b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalidnode.xml
new file mode 100644 (file)
index 0000000..863ef03
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<routes xmlns="http://symfony.com/schema/routing"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
+
+    <foo>bar</foo>
+</routes>
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalidroute.xml b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalidroute.xml
new file mode 100644 (file)
index 0000000..a46961e
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<routes xmlns="http://symfony.com/schema/routing"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
+
+    <route id="blog_show" path="/blog/{slug}">
+        <default key="_controller">MyBundle:Blog:show</default>
+        <requirement key="_method">GET</requirement>
+        <option key="compiler_class">RouteCompiler</option>
+        <foo key="bar">baz</foo>
+    </route>
+</routes>
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/special_route_name.yml b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/special_route_name.yml
new file mode 100644 (file)
index 0000000..78be239
--- /dev/null
@@ -0,0 +1,2 @@
+"#$péß^a|":
+    path: "true"
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validpattern.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validpattern.php
new file mode 100644 (file)
index 0000000..b8bbbb5
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Route;
+
+$collection = new RouteCollection();
+$collection->add('blog_show', new Route(
+    '/blog/{slug}',
+    array('_controller' => 'MyBlogBundle:Blog:show'),
+    array('locale' => '\w+'),
+    array('compiler_class' => 'RouteCompiler'),
+    '{locale}.example.com',
+    array('https'),
+    array('GET','POST','put','OpTiOnS')
+));
+$collection->add('blog_show_legacy', new Route(
+    '/blog/{slug}',
+    array('_controller' => 'MyBlogBundle:Blog:show'),
+    array('_method' => 'GET|POST|put|OpTiOnS', '_scheme' => 'https', 'locale' => '\w+',),
+    array('compiler_class' => 'RouteCompiler'),
+    '{locale}.example.com'
+));
+
+return $collection;
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validpattern.xml b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validpattern.xml
new file mode 100644 (file)
index 0000000..b9f2234
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<routes xmlns="http://symfony.com/schema/routing"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
+
+    <route id="blog_show" path="/blog/{slug}" host="{locale}.example.com" methods="GET|POST  put,OpTiOnS" schemes="hTTps">
+        <default key="_controller">MyBundle:Blog:show</default>
+        <requirement key="locale">\w+</requirement>
+        <option key="compiler_class">RouteCompiler</option>
+    </route>
+
+    <route id="blog_show_legacy" pattern="/blog/{slug}" host="{locale}.example.com">
+        <default key="_controller">MyBundle:Blog:show</default>
+        <default key="slug" xsi:nil="true" />
+        <requirement key="_method">GET|POST|put|OpTiOnS</requirement>
+        <requirement key="_scheme">hTTps</requirement>
+        <requirement key="locale">\w+</requirement>
+        <option key="compiler_class">RouteCompiler</option>
+    </route>
+</routes>
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validpattern.yml b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validpattern.yml
new file mode 100644 (file)
index 0000000..4ada883
--- /dev/null
@@ -0,0 +1,17 @@
+blog_show:
+    path:         /blog/{slug}
+    defaults:     { _controller: "MyBundle:Blog:show" }
+    host:         "{locale}.example.com"
+    requirements: { 'locale': '\w+' }
+    methods:      ['GET','POST','put','OpTiOnS']
+    schemes:      ['https']
+    options:
+        compiler_class: RouteCompiler
+
+blog_show_legacy:
+    pattern:      /blog/{slug}
+    defaults:     { _controller: "MyBundle:Blog:show" }
+    host:         "{locale}.example.com"
+    requirements: { '_method': 'GET|POST|put|OpTiOnS', _scheme: https, 'locale': '\w+' }
+    options:
+        compiler_class: RouteCompiler
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validresource.xml b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validresource.xml
new file mode 100644 (file)
index 0000000..295c3cc
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<routes xmlns="http://symfony.com/schema/routing"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
+
+    <import resource="validpattern.xml" prefix="/{foo}" host="">
+        <default key="foo">123</default>
+        <requirement key="foo">\d+</requirement>
+        <option key="foo">bar</option>
+    </import>
+</routes>
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validresource.yml b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validresource.yml
new file mode 100644 (file)
index 0000000..495ed85
--- /dev/null
@@ -0,0 +1,7 @@
+_blog:
+    resource:     validpattern.yml
+    prefix:       /{foo}
+    defaults:     { 'foo': '123' }
+    requirements: { 'foo': '\d+' }
+    options:      { 'foo': 'bar' }
+    host:         ""
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/withdoctype.xml b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/withdoctype.xml
new file mode 100644 (file)
index 0000000..f217d5b
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0"?>
+<!DOCTYPE foo>
+<foo></foo>
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php
new file mode 100644 (file)
index 0000000..ab5f4cd
--- /dev/null
@@ -0,0 +1,117 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Generator\Dumper;
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\Generator\Dumper\PhpGeneratorDumper;
+use Symfony\Component\Routing\RequestContext;
+
+class PhpGeneratorDumperTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var RouteCollection
+     */
+    private $routeCollection;
+
+    /**
+     * @var PhpGeneratorDumper
+     */
+    private $generatorDumper;
+
+    /**
+     * @var string
+     */
+    private $testTmpFilepath;
+
+    protected function setUp()
+    {
+        parent::setUp();
+
+        $this->routeCollection = new RouteCollection();
+        $this->generatorDumper = new PhpGeneratorDumper($this->routeCollection);
+        $this->testTmpFilepath = sys_get_temp_dir().DIRECTORY_SEPARATOR.'php_generator.php';
+        @unlink($this->testTmpFilepath);
+    }
+
+    protected function tearDown()
+    {
+        parent::tearDown();
+
+        @unlink($this->testTmpFilepath);
+
+        $this->routeCollection = null;
+        $this->generatorDumper = null;
+        $this->testTmpFilepath = null;
+    }
+
+    public function testDumpWithRoutes()
+    {
+        $this->routeCollection->add('Test', new Route('/testing/{foo}'));
+        $this->routeCollection->add('Test2', new Route('/testing2'));
+
+        file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump());
+        include ($this->testTmpFilepath);
+
+        $projectUrlGenerator = new \ProjectUrlGenerator(new RequestContext('/app.php'));
+
+        $absoluteUrlWithParameter    = $projectUrlGenerator->generate('Test', array('foo' => 'bar'), true);
+        $absoluteUrlWithoutParameter = $projectUrlGenerator->generate('Test2', array(), true);
+        $relativeUrlWithParameter    = $projectUrlGenerator->generate('Test', array('foo' => 'bar'), false);
+        $relativeUrlWithoutParameter = $projectUrlGenerator->generate('Test2', array(), false);
+
+        $this->assertEquals($absoluteUrlWithParameter, 'http://localhost/app.php/testing/bar');
+        $this->assertEquals($absoluteUrlWithoutParameter, 'http://localhost/app.php/testing2');
+        $this->assertEquals($relativeUrlWithParameter, '/app.php/testing/bar');
+        $this->assertEquals($relativeUrlWithoutParameter, '/app.php/testing2');
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testDumpWithoutRoutes()
+    {
+        file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump(array('class' => 'WithoutRoutesUrlGenerator')));
+        include ($this->testTmpFilepath);
+
+        $projectUrlGenerator = new \WithoutRoutesUrlGenerator(new RequestContext('/app.php'));
+
+        $projectUrlGenerator->generate('Test', array());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\RouteNotFoundException
+     */
+    public function testGenerateNonExistingRoute()
+    {
+        $this->routeCollection->add('Test', new Route('/test'));
+
+        file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump(array('class' => 'NonExistingRoutesUrlGenerator')));
+        include ($this->testTmpFilepath);
+
+        $projectUrlGenerator = new \NonExistingRoutesUrlGenerator(new RequestContext());
+        $url = $projectUrlGenerator->generate('NonExisting', array());
+    }
+
+    public function testDumpForRouteWithDefaults()
+    {
+        $this->routeCollection->add('Test', new Route('/testing/{foo}', array('foo' => 'bar')));
+
+        file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump(array('class' => 'DefaultRoutesUrlGenerator')));
+        include ($this->testTmpFilepath);
+
+        $projectUrlGenerator = new \DefaultRoutesUrlGenerator(new RequestContext());
+        $url = $projectUrlGenerator->generate('Test', array());
+
+        $this->assertEquals($url, '/testing');
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php
new file mode 100644 (file)
index 0000000..5f8ef49
--- /dev/null
@@ -0,0 +1,635 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Generator;
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\Generator\UrlGenerator;
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
+use Symfony\Component\Routing\RequestContext;
+
+class UrlGeneratorTest extends \PHPUnit_Framework_TestCase
+{
+    public function testAbsoluteUrlWithPort80()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing'));
+        $url = $this->getGenerator($routes)->generate('test', array(), true);
+
+        $this->assertEquals('http://localhost/app.php/testing', $url);
+    }
+
+    public function testAbsoluteSecureUrlWithPort443()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing'));
+        $url = $this->getGenerator($routes, array('scheme' => 'https'))->generate('test', array(), true);
+
+        $this->assertEquals('https://localhost/app.php/testing', $url);
+    }
+
+    public function testAbsoluteUrlWithNonStandardPort()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing'));
+        $url = $this->getGenerator($routes, array('httpPort' => 8080))->generate('test', array(), true);
+
+        $this->assertEquals('http://localhost:8080/app.php/testing', $url);
+    }
+
+    public function testAbsoluteSecureUrlWithNonStandardPort()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing'));
+        $url = $this->getGenerator($routes, array('httpsPort' => 8080, 'scheme' => 'https'))->generate('test', array(), true);
+
+        $this->assertEquals('https://localhost:8080/app.php/testing', $url);
+    }
+
+    public function testRelativeUrlWithoutParameters()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing'));
+        $url = $this->getGenerator($routes)->generate('test', array(), false);
+
+        $this->assertEquals('/app.php/testing', $url);
+    }
+
+    public function testRelativeUrlWithParameter()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing/{foo}'));
+        $url = $this->getGenerator($routes)->generate('test', array('foo' => 'bar'), false);
+
+        $this->assertEquals('/app.php/testing/bar', $url);
+    }
+
+    public function testRelativeUrlWithNullParameter()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing.{format}', array('format' => null)));
+        $url = $this->getGenerator($routes)->generate('test', array(), false);
+
+        $this->assertEquals('/app.php/testing', $url);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException
+     */
+    public function testRelativeUrlWithNullParameterButNotOptional()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing/{foo}/bar', array('foo' => null)));
+        // This must raise an exception because the default requirement for "foo" is "[^/]+" which is not met with these params.
+        // Generating path "/testing//bar" would be wrong as matching this route would fail.
+        $this->getGenerator($routes)->generate('test', array(), false);
+    }
+
+    public function testRelativeUrlWithOptionalZeroParameter()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing/{page}'));
+        $url = $this->getGenerator($routes)->generate('test', array('page' => 0), false);
+
+        $this->assertEquals('/app.php/testing/0', $url);
+    }
+
+    public function testNotPassedOptionalParameterInBetween()
+    {
+        $routes = $this->getRoutes('test', new Route('/{slug}/{page}', array('slug' => 'index', 'page' => 0)));
+        $this->assertSame('/app.php/index/1', $this->getGenerator($routes)->generate('test', array('page' => 1)));
+        $this->assertSame('/app.php/', $this->getGenerator($routes)->generate('test'));
+    }
+
+    public function testRelativeUrlWithExtraParameters()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing'));
+        $url = $this->getGenerator($routes)->generate('test', array('foo' => 'bar'), false);
+
+        $this->assertEquals('/app.php/testing?foo=bar', $url);
+    }
+
+    public function testAbsoluteUrlWithExtraParameters()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing'));
+        $url = $this->getGenerator($routes)->generate('test', array('foo' => 'bar'), true);
+
+        $this->assertEquals('http://localhost/app.php/testing?foo=bar', $url);
+    }
+
+    public function testUrlWithNullExtraParameters()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing'));
+        $url = $this->getGenerator($routes)->generate('test', array('foo' => null), true);
+
+        $this->assertEquals('http://localhost/app.php/testing', $url);
+    }
+
+    public function testUrlWithExtraParametersFromGlobals()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing'));
+        $generator = $this->getGenerator($routes);
+        $context = new RequestContext('/app.php');
+        $context->setParameter('bar', 'bar');
+        $generator->setContext($context);
+        $url = $generator->generate('test', array('foo' => 'bar'));
+
+        $this->assertEquals('/app.php/testing?foo=bar', $url);
+    }
+
+    public function testUrlWithGlobalParameter()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing/{foo}'));
+        $generator = $this->getGenerator($routes);
+        $context = new RequestContext('/app.php');
+        $context->setParameter('foo', 'bar');
+        $generator->setContext($context);
+        $url = $generator->generate('test', array());
+
+        $this->assertEquals('/app.php/testing/bar', $url);
+    }
+
+    public function testGlobalParameterHasHigherPriorityThanDefault()
+    {
+        $routes = $this->getRoutes('test', new Route('/{_locale}', array('_locale' => 'en')));
+        $generator = $this->getGenerator($routes);
+        $context = new RequestContext('/app.php');
+        $context->setParameter('_locale', 'de');
+        $generator->setContext($context);
+        $url = $generator->generate('test', array());
+
+        $this->assertSame('/app.php/de', $url);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\RouteNotFoundException
+     */
+    public function testGenerateWithoutRoutes()
+    {
+        $routes = $this->getRoutes('foo', new Route('/testing/{foo}'));
+        $this->getGenerator($routes)->generate('test', array(), true);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\MissingMandatoryParametersException
+     */
+    public function testGenerateForRouteWithoutMandatoryParameter()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing/{foo}'));
+        $this->getGenerator($routes)->generate('test', array(), true);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException
+     */
+    public function testGenerateForRouteWithInvalidOptionalParameter()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing/{foo}', array('foo' => '1'), array('foo' => 'd+')));
+        $this->getGenerator($routes)->generate('test', array('foo' => 'bar'), true);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException
+     */
+    public function testGenerateForRouteWithInvalidParameter()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing/{foo}', array(), array('foo' => '1|2')));
+        $this->getGenerator($routes)->generate('test', array('foo' => '0'), true);
+    }
+
+    public function testGenerateForRouteWithInvalidOptionalParameterNonStrict()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing/{foo}', array('foo' => '1'), array('foo' => 'd+')));
+        $generator = $this->getGenerator($routes);
+        $generator->setStrictRequirements(false);
+        $this->assertNull($generator->generate('test', array('foo' => 'bar'), true));
+    }
+
+    public function testGenerateForRouteWithInvalidOptionalParameterNonStrictWithLogger()
+    {
+        if (!interface_exists('Psr\Log\LoggerInterface')) {
+            $this->markTestSkipped('The "psr/log" package is not available');
+        }
+
+        $routes = $this->getRoutes('test', new Route('/testing/{foo}', array('foo' => '1'), array('foo' => 'd+')));
+        $logger = $this->getMock('Psr\Log\LoggerInterface');
+        $logger->expects($this->once())
+            ->method('error');
+        $generator = $this->getGenerator($routes, array(), $logger);
+        $generator->setStrictRequirements(false);
+        $this->assertNull($generator->generate('test', array('foo' => 'bar'), true));
+    }
+
+    public function testGenerateForRouteWithInvalidParameterButDisabledRequirementsCheck()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing/{foo}', array('foo' => '1'), array('foo' => 'd+')));
+        $generator = $this->getGenerator($routes);
+        $generator->setStrictRequirements(null);
+        $this->assertSame('/app.php/testing/bar', $generator->generate('test', array('foo' => 'bar')));
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException
+     */
+    public function testGenerateForRouteWithInvalidMandatoryParameter()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing/{foo}', array(), array('foo' => 'd+')));
+        $this->getGenerator($routes)->generate('test', array('foo' => 'bar'), true);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException
+     */
+    public function testRequiredParamAndEmptyPassed()
+    {
+        $routes = $this->getRoutes('test', new Route('/{slug}', array(), array('slug' => '.+')));
+        $this->getGenerator($routes)->generate('test', array('slug' => ''));
+    }
+
+    public function testSchemeRequirementDoesNothingIfSameCurrentScheme()
+    {
+        $routes = $this->getRoutes('test', new Route('/', array(), array('_scheme' => 'http')));
+        $this->assertEquals('/app.php/', $this->getGenerator($routes)->generate('test'));
+
+        $routes = $this->getRoutes('test', new Route('/', array(), array('_scheme' => 'https')));
+        $this->assertEquals('/app.php/', $this->getGenerator($routes, array('scheme' => 'https'))->generate('test'));
+    }
+
+    public function testSchemeRequirementForcesAbsoluteUrl()
+    {
+        $routes = $this->getRoutes('test', new Route('/', array(), array('_scheme' => 'https')));
+        $this->assertEquals('https://localhost/app.php/', $this->getGenerator($routes)->generate('test'));
+
+        $routes = $this->getRoutes('test', new Route('/', array(), array('_scheme' => 'http')));
+        $this->assertEquals('http://localhost/app.php/', $this->getGenerator($routes, array('scheme' => 'https'))->generate('test'));
+    }
+
+    public function testPathWithTwoStartingSlashes()
+    {
+        $routes = $this->getRoutes('test', new Route('//path-and-not-domain'));
+
+        // this must not generate '//path-and-not-domain' because that would be a network path
+        $this->assertSame('/path-and-not-domain', $this->getGenerator($routes, array('BaseUrl' => ''))->generate('test'));
+    }
+
+    public function testNoTrailingSlashForMultipleOptionalParameters()
+    {
+        $routes = $this->getRoutes('test', new Route('/category/{slug1}/{slug2}/{slug3}', array('slug2' => null, 'slug3' => null)));
+
+        $this->assertEquals('/app.php/category/foo', $this->getGenerator($routes)->generate('test', array('slug1' => 'foo')));
+    }
+
+    public function testWithAnIntegerAsADefaultValue()
+    {
+        $routes = $this->getRoutes('test', new Route('/{default}', array('default' => 0)));
+
+        $this->assertEquals('/app.php/foo', $this->getGenerator($routes)->generate('test', array('default' => 'foo')));
+    }
+
+    public function testNullForOptionalParameterIsIgnored()
+    {
+        $routes = $this->getRoutes('test', new Route('/test/{default}', array('default' => 0)));
+
+        $this->assertEquals('/app.php/test', $this->getGenerator($routes)->generate('test', array('default' => null)));
+    }
+
+    public function testQueryParamSameAsDefault()
+    {
+        $routes = $this->getRoutes('test', new Route('/test', array('default' => 'value')));
+
+        $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test', array('default' => 'foo')));
+        $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test', array('default' => 'value')));
+        $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test'));
+    }
+
+    public function testGenerateWithSpecialRouteName()
+    {
+        $routes = $this->getRoutes('$péß^a|', new Route('/bar'));
+
+        $this->assertSame('/app.php/bar', $this->getGenerator($routes)->generate('$péß^a|'));
+    }
+
+    public function testUrlEncoding()
+    {
+        // This tests the encoding of reserved characters that are used for delimiting of URI components (defined in RFC 3986)
+        // and other special ASCII chars. These chars are tested as static text path, variable path and query param.
+        $chars = '@:[]/()*\'" +,;-._~&$<>|{}%\\^`!?foo=bar#id';
+        $routes = $this->getRoutes('test', new Route("/$chars/{varpath}", array(), array('varpath' => '.+')));
+        $this->assertSame('/app.php/@:%5B%5D/%28%29*%27%22%20+,;-._~%26%24%3C%3E|%7B%7D%25%5C%5E%60!%3Ffoo=bar%23id'
+           .'/@:%5B%5D/%28%29*%27%22%20+,;-._~%26%24%3C%3E|%7B%7D%25%5C%5E%60!%3Ffoo=bar%23id'
+           .'?query=%40%3A%5B%5D%2F%28%29%2A%27%22+%2B%2C%3B-._%7E%26%24%3C%3E%7C%7B%7D%25%5C%5E%60%21%3Ffoo%3Dbar%23id',
+            $this->getGenerator($routes)->generate('test', array(
+                'varpath' => $chars,
+                'query' => $chars
+            ))
+        );
+    }
+
+    public function testEncodingOfRelativePathSegments()
+    {
+        $routes = $this->getRoutes('test', new Route('/dir/../dir/..'));
+        $this->assertSame('/app.php/dir/%2E%2E/dir/%2E%2E', $this->getGenerator($routes)->generate('test'));
+        $routes = $this->getRoutes('test', new Route('/dir/./dir/.'));
+        $this->assertSame('/app.php/dir/%2E/dir/%2E', $this->getGenerator($routes)->generate('test'));
+        $routes = $this->getRoutes('test', new Route('/a./.a/a../..a/...'));
+        $this->assertSame('/app.php/a./.a/a../..a/...', $this->getGenerator($routes)->generate('test'));
+    }
+
+    public function testAdjacentVariables()
+    {
+        $routes = $this->getRoutes('test', new Route('/{x}{y}{z}.{_format}', array('z' => 'default-z', '_format' => 'html'), array('y' => '\d+')));
+        $generator = $this->getGenerator($routes);
+        $this->assertSame('/app.php/foo123', $generator->generate('test', array('x' => 'foo', 'y' => '123')));
+        $this->assertSame('/app.php/foo123bar.xml', $generator->generate('test', array('x' => 'foo', 'y' => '123', 'z' => 'bar', '_format' => 'xml')));
+
+        // The default requirement for 'x' should not allow the separator '.' in this case because it would otherwise match everything
+        // and following optional variables like _format could never match.
+        $this->setExpectedException('Symfony\Component\Routing\Exception\InvalidParameterException');
+        $generator->generate('test', array('x' => 'do.t', 'y' => '123', 'z' => 'bar', '_format' => 'xml'));
+    }
+
+    public function testOptionalVariableWithNoRealSeparator()
+    {
+        $routes = $this->getRoutes('test', new Route('/get{what}', array('what' => 'All')));
+        $generator = $this->getGenerator($routes);
+
+        $this->assertSame('/app.php/get', $generator->generate('test'));
+        $this->assertSame('/app.php/getSites', $generator->generate('test', array('what' => 'Sites')));
+    }
+
+    public function testRequiredVariableWithNoRealSeparator()
+    {
+        $routes = $this->getRoutes('test', new Route('/get{what}Suffix'));
+        $generator = $this->getGenerator($routes);
+
+        $this->assertSame('/app.php/getSitesSuffix', $generator->generate('test', array('what' => 'Sites')));
+    }
+
+    public function testDefaultRequirementOfVariable()
+    {
+        $routes = $this->getRoutes('test', new Route('/{page}.{_format}'));
+        $generator = $this->getGenerator($routes);
+
+        $this->assertSame('/app.php/index.mobile.html', $generator->generate('test', array('page' => 'index', '_format' => 'mobile.html')));
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException
+     */
+    public function testDefaultRequirementOfVariableDisallowsSlash()
+    {
+        $routes = $this->getRoutes('test', new Route('/{page}.{_format}'));
+        $this->getGenerator($routes)->generate('test', array('page' => 'index', '_format' => 'sl/ash'));
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException
+     */
+    public function testDefaultRequirementOfVariableDisallowsNextSeparator()
+    {
+        $routes = $this->getRoutes('test', new Route('/{page}.{_format}'));
+        $this->getGenerator($routes)->generate('test', array('page' => 'do.t', '_format' => 'html'));
+    }
+
+    public function testWithHostDifferentFromContext()
+    {
+        $routes = $this->getRoutes('test', new Route('/{name}', array(), array(), array(), '{locale}.example.com'));
+
+        $this->assertEquals('//fr.example.com/app.php/Fabien', $this->getGenerator($routes)->generate('test', array('name' =>'Fabien', 'locale' => 'fr')));
+    }
+
+    public function testWithHostSameAsContext()
+    {
+        $routes = $this->getRoutes('test', new Route('/{name}', array(), array(), array(), '{locale}.example.com'));
+
+        $this->assertEquals('/app.php/Fabien', $this->getGenerator($routes, array('host' => 'fr.example.com'))->generate('test', array('name' =>'Fabien', 'locale' => 'fr')));
+    }
+
+    public function testWithHostSameAsContextAndAbsolute()
+    {
+        $routes = $this->getRoutes('test', new Route('/{name}', array(), array(), array(), '{locale}.example.com'));
+
+        $this->assertEquals('http://fr.example.com/app.php/Fabien', $this->getGenerator($routes, array('host' => 'fr.example.com'))->generate('test', array('name' =>'Fabien', 'locale' => 'fr'), true));
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException
+     */
+    public function testUrlWithInvalidParameterInHost()
+    {
+        $routes = $this->getRoutes('test', new Route('/', array(), array('foo' => 'bar'), array(), '{foo}.example.com'));
+        $this->getGenerator($routes)->generate('test', array('foo' => 'baz'), false);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException
+     */
+    public function testUrlWithInvalidParameterInHostWhenParamHasADefaultValue()
+    {
+        $routes = $this->getRoutes('test', new Route('/', array('foo' => 'bar'), array('foo' => 'bar'), array(), '{foo}.example.com'));
+        $this->getGenerator($routes)->generate('test', array('foo' => 'baz'), false);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException
+     */
+    public function testUrlWithInvalidParameterEqualsDefaultValueInHost()
+    {
+        $routes = $this->getRoutes('test', new Route('/', array('foo' => 'baz'), array('foo' => 'bar'), array(), '{foo}.example.com'));
+        $this->getGenerator($routes)->generate('test', array('foo' => 'baz'), false);
+    }
+
+    public function testUrlWithInvalidParameterInHostInNonStrictMode()
+    {
+        $routes = $this->getRoutes('test', new Route('/', array(), array('foo' => 'bar'), array(), '{foo}.example.com'));
+        $generator = $this->getGenerator($routes);
+        $generator->setStrictRequirements(false);
+        $this->assertNull($generator->generate('test', array('foo' => 'baz'), false));
+    }
+
+    public function testGenerateNetworkPath()
+    {
+        $routes = $this->getRoutes('test', new Route('/{name}', array(), array('_scheme' => 'http'), array(), '{locale}.example.com'));
+
+        $this->assertSame('//fr.example.com/app.php/Fabien', $this->getGenerator($routes)->generate('test',
+            array('name' =>'Fabien', 'locale' => 'fr'), UrlGeneratorInterface::NETWORK_PATH), 'network path with different host'
+        );
+        $this->assertSame('//fr.example.com/app.php/Fabien?query=string', $this->getGenerator($routes, array('host' => 'fr.example.com'))->generate('test',
+            array('name' =>'Fabien', 'locale' => 'fr', 'query' => 'string'), UrlGeneratorInterface::NETWORK_PATH), 'network path although host same as context'
+        );
+        $this->assertSame('http://fr.example.com/app.php/Fabien', $this->getGenerator($routes, array('scheme' => 'https'))->generate('test',
+            array('name' =>'Fabien', 'locale' => 'fr'), UrlGeneratorInterface::NETWORK_PATH), 'absolute URL because scheme requirement does not match context'
+        );
+        $this->assertSame('http://fr.example.com/app.php/Fabien', $this->getGenerator($routes)->generate('test',
+            array('name' =>'Fabien', 'locale' => 'fr'), UrlGeneratorInterface::ABSOLUTE_URL), 'absolute URL with same scheme because it is requested'
+        );
+    }
+
+    public function testGenerateRelativePath()
+    {
+        $routes = new RouteCollection();
+        $routes->add('article', new Route('/{author}/{article}/'));
+        $routes->add('comments', new Route('/{author}/{article}/comments'));
+        $routes->add('host', new Route('/{article}', array(), array(), array(), '{author}.example.com'));
+        $routes->add('scheme', new Route('/{author}', array(), array('_scheme' => 'https')));
+        $routes->add('unrelated', new Route('/about'));
+
+        $generator = $this->getGenerator($routes, array('host' => 'example.com', 'pathInfo' => '/fabien/symfony-is-great/'));
+
+        $this->assertSame('comments', $generator->generate('comments',
+            array('author' =>'fabien', 'article' => 'symfony-is-great'), UrlGeneratorInterface::RELATIVE_PATH)
+        );
+        $this->assertSame('comments?page=2', $generator->generate('comments',
+            array('author' =>'fabien', 'article' => 'symfony-is-great', 'page' => 2), UrlGeneratorInterface::RELATIVE_PATH)
+        );
+        $this->assertSame('../twig-is-great/', $generator->generate('article',
+            array('author' =>'fabien', 'article' => 'twig-is-great'), UrlGeneratorInterface::RELATIVE_PATH)
+        );
+        $this->assertSame('../../bernhard/forms-are-great/', $generator->generate('article',
+            array('author' =>'bernhard', 'article' => 'forms-are-great'), UrlGeneratorInterface::RELATIVE_PATH)
+        );
+        $this->assertSame('//bernhard.example.com/app.php/forms-are-great', $generator->generate('host',
+            array('author' =>'bernhard', 'article' => 'forms-are-great'), UrlGeneratorInterface::RELATIVE_PATH)
+        );
+        $this->assertSame('https://example.com/app.php/bernhard', $generator->generate('scheme',
+            array('author' =>'bernhard'), UrlGeneratorInterface::RELATIVE_PATH)
+        );
+        $this->assertSame('../../about', $generator->generate('unrelated',
+            array(), UrlGeneratorInterface::RELATIVE_PATH)
+        );
+    }
+
+    /**
+     * @dataProvider provideRelativePaths
+     */
+    public function testGetRelativePath($sourcePath, $targetPath, $expectedPath)
+    {
+        $this->assertSame($expectedPath, UrlGenerator::getRelativePath($sourcePath, $targetPath));
+    }
+
+    public function provideRelativePaths()
+    {
+        return array(
+            array(
+                '/same/dir/',
+                '/same/dir/',
+                ''
+            ),
+            array(
+                '/same/file',
+                '/same/file',
+                ''
+            ),
+            array(
+                '/',
+                '/file',
+                'file'
+            ),
+            array(
+                '/',
+                '/dir/file',
+                'dir/file'
+            ),
+            array(
+                '/dir/file.html',
+                '/dir/different-file.html',
+                'different-file.html'
+            ),
+            array(
+                '/same/dir/extra-file',
+                '/same/dir/',
+                './'
+            ),
+            array(
+                '/parent/dir/',
+                '/parent/',
+                '../'
+            ),
+            array(
+                '/parent/dir/extra-file',
+                '/parent/',
+                '../'
+            ),
+            array(
+                '/a/b/',
+                '/x/y/z/',
+                '../../x/y/z/'
+            ),
+            array(
+                '/a/b/c/d/e',
+                '/a/c/d',
+                '../../../c/d'
+            ),
+            array(
+                '/a/b/c//',
+                '/a/b/c/',
+                '../'
+            ),
+            array(
+                '/a/b/c/',
+                '/a/b/c//',
+                './/'
+            ),
+            array(
+                '/root/a/b/c/',
+                '/root/x/b/c/',
+                '../../../x/b/c/'
+            ),
+            array(
+                '/a/b/c/d/',
+                '/a',
+                '../../../../a'
+            ),
+            array(
+                '/special-chars/sp%20ce/1€/mäh/e=mc²',
+                '/special-chars/sp%20ce/1€/<µ>/e=mc²',
+                '../<µ>/e=mc²'
+            ),
+            array(
+                'not-rooted',
+                'dir/file',
+                'dir/file'
+            ),
+            array(
+                '//dir/',
+                '',
+                '../../'
+            ),
+            array(
+                '/dir/',
+                '/dir/file:with-colon',
+                './file:with-colon'
+            ),
+            array(
+                '/dir/',
+                '/dir/subdir/file:with-colon',
+                'subdir/file:with-colon'
+            ),
+            array(
+                '/dir/',
+                '/dir/:subdir/',
+                './:subdir/'
+            ),
+        );
+    }
+
+    protected function getGenerator(RouteCollection $routes, array $parameters = array(), $logger = null)
+    {
+        $context = new RequestContext('/app.php');
+        foreach ($parameters as $key => $value) {
+            $method = 'set'.$key;
+            $context->$method($value);
+        }
+        $generator = new UrlGenerator($routes, $context, $logger);
+
+        return $generator;
+    }
+
+    protected function getRoutes($name, Route $route)
+    {
+        $routes = new RouteCollection();
+        $routes->add($name, $route);
+
+        return $routes;
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/AbstractAnnotationLoaderTest.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/AbstractAnnotationLoaderTest.php
new file mode 100644 (file)
index 0000000..c927ae4
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Loader;
+
+abstract class AbstractAnnotationLoaderTest extends \PHPUnit_Framework_TestCase
+{
+    protected function setUp()
+    {
+        if (!class_exists('Doctrine\\Common\\Version')) {
+            $this->markTestSkipped('Doctrine is not available.');
+        }
+    }
+
+    public function getReader()
+    {
+        return $this->getMockBuilder('Doctrine\Common\Annotations\Reader')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+    }
+
+    public function getClassLoader($reader)
+    {
+        return $this->getMockBuilder('Symfony\Component\Routing\Loader\AnnotationClassLoader')
+            ->setConstructorArgs(array($reader))
+            ->getMockForAbstractClass()
+        ;
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php
new file mode 100644 (file)
index 0000000..31c43f5
--- /dev/null
@@ -0,0 +1,119 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Loader;
+
+class AnnotationClassLoaderTest extends AbstractAnnotationLoaderTest
+{
+    protected $loader;
+
+    protected function setUp()
+    {
+        parent::setUp();
+
+        $this->reader = $this->getReader();
+        $this->loader = $this->getClassLoader($this->reader);
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testLoadMissingClass()
+    {
+        $this->loader->load('MissingClass');
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testLoadAbstractClass()
+    {
+        $this->loader->load('Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\AbstractClass');
+    }
+
+    /**
+     * @dataProvider provideTestSupportsChecksResource
+     */
+    public function testSupportsChecksResource($resource, $expectedSupports)
+    {
+        $this->assertSame($expectedSupports, $this->loader->supports($resource), '->supports() returns true if the resource is loadable');
+    }
+
+    public function provideTestSupportsChecksResource()
+    {
+        return array(
+            array('class', true),
+            array('\fully\qualified\class\name', true),
+            array('namespaced\class\without\leading\slash', true),
+            array('ÿClassWithLegalSpecialCharacters', true),
+            array('5', false),
+            array('foo.foo', false),
+            array(null, false),
+        );
+    }
+
+    public function testSupportsChecksTypeIfSpecified()
+    {
+        $this->assertTrue($this->loader->supports('class', 'annotation'), '->supports() checks the resource type if specified');
+        $this->assertFalse($this->loader->supports('class', 'foo'), '->supports() checks the resource type if specified');
+    }
+
+    public function getLoadTests()
+    {
+        return array(
+            array(
+                'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass',
+                array('name'=>'route1'),
+                array('arg2' => 'defaultValue2', 'arg3' =>'defaultValue3')
+            ),
+            array(
+                'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass',
+                array('name'=>'route1', 'defaults' => array('arg2' => 'foo')),
+                array('arg2' => 'defaultValue2', 'arg3' =>'defaultValue3')
+            ),
+        );
+    }
+
+    /**
+     * @dataProvider getLoadTests
+     */
+    public function testLoad($className, $routeDatas = array(), $methodArgs = array())
+    {
+        $routeDatas = array_replace(array(
+            'name'         => 'route',
+            'path'         => '/',
+            'requirements' => array(),
+            'options'      => array(),
+            'defaults'     => array(),
+            'schemes'      => array(),
+            'methods'      => array(),
+        ), $routeDatas);
+
+        $this->reader
+            ->expects($this->once())
+            ->method('getMethodAnnotations')
+            ->will($this->returnValue(array($this->getAnnotatedRoute($routeDatas))))
+        ;
+        $routeCollection = $this->loader->load($className);
+        $route = $routeCollection->get($routeDatas['name']);
+
+        $this->assertSame($routeDatas['path'], $route->getPath(), '->load preserves path annotation');
+        $this->assertSame($routeDatas['requirements'],$route->getRequirements(), '->load preserves requirements annotation');
+        $this->assertCount(0, array_intersect($route->getOptions(), $routeDatas['options']), '->load preserves options annotation');
+        $this->assertSame(array_replace($routeDatas['defaults'], $methodArgs), $route->getDefaults(), '->load preserves defaults annotation');
+    }
+
+    private function getAnnotatedRoute($datas)
+    {
+        return new \Symfony\Component\Routing\Annotation\Route($datas);
+    }
+
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/AnnotationDirectoryLoaderTest.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/AnnotationDirectoryLoaderTest.php
new file mode 100644 (file)
index 0000000..29126ba
--- /dev/null
@@ -0,0 +1,53 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Loader;
+
+use Symfony\Component\Routing\Loader\AnnotationDirectoryLoader;
+use Symfony\Component\Config\FileLocator;
+
+class AnnotationDirectoryLoaderTest extends AbstractAnnotationLoaderTest
+{
+    protected $loader;
+    protected $reader;
+
+    protected function setUp()
+    {
+        parent::setUp();
+
+        $this->reader = $this->getReader();
+        $this->loader = new AnnotationDirectoryLoader(new FileLocator(), $this->getClassLoader($this->reader));
+    }
+
+    public function testLoad()
+    {
+        $this->reader->expects($this->exactly(2))->method('getClassAnnotation');
+
+        $this->reader
+            ->expects($this->any())
+            ->method('getMethodAnnotations')
+            ->will($this->returnValue(array()))
+        ;
+
+        $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses');
+    }
+
+    public function testSupports()
+    {
+        $fixturesDir = __DIR__.'/../Fixtures';
+
+        $this->assertTrue($this->loader->supports($fixturesDir), '->supports() returns true if the resource is loadable');
+        $this->assertFalse($this->loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');
+
+        $this->assertTrue($this->loader->supports($fixturesDir, 'annotation'), '->supports() checks the resource type if specified');
+        $this->assertFalse($this->loader->supports($fixturesDir, 'foo'), '->supports() checks the resource type if specified');
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php
new file mode 100644 (file)
index 0000000..f0a8a0e
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Loader;
+
+use Symfony\Component\Routing\Loader\AnnotationFileLoader;
+use Symfony\Component\Config\FileLocator;
+
+class AnnotationFileLoaderTest extends AbstractAnnotationLoaderTest
+{
+    protected $loader;
+    protected $reader;
+
+    protected function setUp()
+    {
+        parent::setUp();
+
+        $this->reader = $this->getReader();
+        $this->loader = new AnnotationFileLoader(new FileLocator(), $this->getClassLoader($this->reader));
+    }
+
+    public function testLoad()
+    {
+        $this->reader->expects($this->once())->method('getClassAnnotation');
+
+        $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/FooClass.php');
+    }
+
+    public function testSupports()
+    {
+        $fixture = __DIR__.'/../Fixtures/annotated.php';
+
+        $this->assertTrue($this->loader->supports($fixture), '->supports() returns true if the resource is loadable');
+        $this->assertFalse($this->loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');
+
+        $this->assertTrue($this->loader->supports($fixture, 'annotation'), '->supports() checks the resource type if specified');
+        $this->assertFalse($this->loader->supports($fixture, 'foo'), '->supports() checks the resource type if specified');
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/ClosureLoaderTest.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/ClosureLoaderTest.php
new file mode 100644 (file)
index 0000000..64d1b08
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Loader;
+
+use Symfony\Component\Routing\Loader\ClosureLoader;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+class ClosureLoaderTest extends \PHPUnit_Framework_TestCase
+{
+    protected function setUp()
+    {
+        if (!class_exists('Symfony\Component\Config\FileLocator')) {
+            $this->markTestSkipped('The "Config" component is not available');
+        }
+    }
+
+    public function testSupports()
+    {
+        $loader = new ClosureLoader();
+
+        $closure = function () {};
+
+        $this->assertTrue($loader->supports($closure), '->supports() returns true if the resource is loadable');
+        $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');
+
+        $this->assertTrue($loader->supports($closure, 'closure'), '->supports() checks the resource type if specified');
+        $this->assertFalse($loader->supports($closure, 'foo'), '->supports() checks the resource type if specified');
+    }
+
+    public function testLoad()
+    {
+        $loader = new ClosureLoader();
+
+        $route = new Route('/');
+        $routes = $loader->load(function () use ($route) {
+            $routes = new RouteCollection();
+
+            $routes->add('foo', $route);
+
+            return $routes;
+        });
+
+        $this->assertEquals($route, $routes->get('foo'), '->load() loads a \Closure resource');
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php
new file mode 100644 (file)
index 0000000..18b166f
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Loader;
+
+use Symfony\Component\Config\FileLocator;
+use Symfony\Component\Routing\Loader\PhpFileLoader;
+
+class PhpFileLoaderTest extends \PHPUnit_Framework_TestCase
+{
+    protected function setUp()
+    {
+        if (!class_exists('Symfony\Component\Config\FileLocator')) {
+            $this->markTestSkipped('The "Config" component is not available');
+        }
+    }
+
+    public function testSupports()
+    {
+        $loader = new PhpFileLoader($this->getMock('Symfony\Component\Config\FileLocator'));
+
+        $this->assertTrue($loader->supports('foo.php'), '->supports() returns true if the resource is loadable');
+        $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');
+
+        $this->assertTrue($loader->supports('foo.php', 'php'), '->supports() checks the resource type if specified');
+        $this->assertFalse($loader->supports('foo.php', 'foo'), '->supports() checks the resource type if specified');
+    }
+
+    public function testLoadWithRoute()
+    {
+        $loader = new PhpFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+        $routeCollection = $loader->load('validpattern.php');
+        $routes = $routeCollection->all();
+
+        $this->assertCount(2, $routes, 'Two routes are loaded');
+        $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
+
+        foreach ($routes as $route) {
+            $this->assertSame('/blog/{slug}', $route->getPath());
+            $this->assertSame('MyBlogBundle:Blog:show', $route->getDefault('_controller'));
+            $this->assertSame('{locale}.example.com', $route->getHost());
+            $this->assertSame('RouteCompiler', $route->getOption('compiler_class'));
+            $this->assertEquals(array('GET', 'POST', 'PUT', 'OPTIONS'), $route->getMethods());
+            $this->assertEquals(array('https'), $route->getSchemes());
+        }
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php
new file mode 100644 (file)
index 0000000..9f038c1
--- /dev/null
@@ -0,0 +1,127 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Loader;
+
+use Symfony\Component\Config\FileLocator;
+use Symfony\Component\Routing\Loader\XmlFileLoader;
+use Symfony\Component\Routing\Tests\Fixtures\CustomXmlFileLoader;
+
+class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
+{
+    protected function setUp()
+    {
+        if (!class_exists('Symfony\Component\Config\FileLocator')) {
+            $this->markTestSkipped('The "Config" component is not available');
+        }
+    }
+
+    public function testSupports()
+    {
+        $loader = new XmlFileLoader($this->getMock('Symfony\Component\Config\FileLocator'));
+
+        $this->assertTrue($loader->supports('foo.xml'), '->supports() returns true if the resource is loadable');
+        $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');
+
+        $this->assertTrue($loader->supports('foo.xml', 'xml'), '->supports() checks the resource type if specified');
+        $this->assertFalse($loader->supports('foo.xml', 'foo'), '->supports() checks the resource type if specified');
+    }
+
+    public function testLoadWithRoute()
+    {
+        $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+        $routeCollection = $loader->load('validpattern.xml');
+        $routes = $routeCollection->all();
+
+        $this->assertCount(2, $routes, 'Two routes are loaded');
+        $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
+
+        foreach ($routes as $route) {
+            $this->assertSame('/blog/{slug}', $route->getPath());
+            $this->assertSame('{locale}.example.com', $route->getHost());
+            $this->assertSame('MyBundle:Blog:show', $route->getDefault('_controller'));
+            $this->assertSame('\w+', $route->getRequirement('locale'));
+            $this->assertSame('RouteCompiler', $route->getOption('compiler_class'));
+            $this->assertEquals(array('GET', 'POST', 'PUT', 'OPTIONS'), $route->getMethods());
+            $this->assertEquals(array('https'), $route->getSchemes());
+        }
+    }
+
+    public function testLoadWithNamespacePrefix()
+    {
+        $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+        $routeCollection = $loader->load('namespaceprefix.xml');
+
+        $this->assertCount(1, $routeCollection->all(), 'One route is loaded');
+
+        $route = $routeCollection->get('blog_show');
+        $this->assertSame('/blog/{slug}', $route->getPath());
+        $this->assertSame('{_locale}.example.com', $route->getHost());
+        $this->assertSame('MyBundle:Blog:show', $route->getDefault('_controller'));
+        $this->assertSame('\w+', $route->getRequirement('slug'));
+        $this->assertSame('en|fr|de', $route->getRequirement('_locale'));
+        $this->assertSame(null, $route->getDefault('slug'));
+        $this->assertSame('RouteCompiler', $route->getOption('compiler_class'));
+    }
+
+    public function testLoadWithImport()
+    {
+        $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+        $routeCollection = $loader->load('validresource.xml');
+        $routes = $routeCollection->all();
+
+        $this->assertCount(2, $routes, 'Two routes are loaded');
+        $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
+
+        foreach ($routes as $route) {
+            $this->assertSame('/{foo}/blog/{slug}', $route->getPath());
+            $this->assertSame('123', $route->getDefault('foo'));
+            $this->assertSame('\d+', $route->getRequirement('foo'));
+            $this->assertSame('bar', $route->getOption('foo'));
+            $this->assertSame('', $route->getHost());
+        }
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     * @dataProvider getPathsToInvalidFiles
+     */
+    public function testLoadThrowsExceptionWithInvalidFile($filePath)
+    {
+        $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+        $loader->load($filePath);
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     * @dataProvider getPathsToInvalidFiles
+     */
+    public function testLoadThrowsExceptionWithInvalidFileEvenWithoutSchemaValidation($filePath)
+    {
+        $loader = new CustomXmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+        $loader->load($filePath);
+    }
+
+    public function getPathsToInvalidFiles()
+    {
+        return array(array('nonvalidnode.xml'), array('nonvalidroute.xml'), array('nonvalid.xml'), array('missing_id.xml'), array('missing_path.xml'));
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     * @expectedExceptionMessage Document types are not allowed.
+     */
+    public function testDocTypeIsNotAllowed()
+    {
+        $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+        $loader->load('withdoctype.xml');
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php
new file mode 100644 (file)
index 0000000..a3e934c
--- /dev/null
@@ -0,0 +1,113 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Loader;
+
+use Symfony\Component\Config\FileLocator;
+use Symfony\Component\Routing\Loader\YamlFileLoader;
+use Symfony\Component\Config\Resource\FileResource;
+
+class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
+{
+    protected function setUp()
+    {
+        if (!class_exists('Symfony\Component\Config\FileLocator')) {
+            $this->markTestSkipped('The "Config" component is not available');
+        }
+
+        if (!class_exists('Symfony\Component\Yaml\Yaml')) {
+            $this->markTestSkipped('The "Yaml" component is not available');
+        }
+    }
+
+    public function testSupports()
+    {
+        $loader = new YamlFileLoader($this->getMock('Symfony\Component\Config\FileLocator'));
+
+        $this->assertTrue($loader->supports('foo.yml'), '->supports() returns true if the resource is loadable');
+        $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');
+
+        $this->assertTrue($loader->supports('foo.yml', 'yaml'), '->supports() checks the resource type if specified');
+        $this->assertFalse($loader->supports('foo.yml', 'foo'), '->supports() checks the resource type if specified');
+    }
+
+    public function testLoadDoesNothingIfEmpty()
+    {
+        $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+        $collection = $loader->load('empty.yml');
+
+        $this->assertEquals(array(), $collection->all());
+        $this->assertEquals(array(new FileResource(realpath(__DIR__.'/../Fixtures/empty.yml'))), $collection->getResources());
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     * @dataProvider getPathsToInvalidFiles
+     */
+    public function testLoadThrowsExceptionWithInvalidFile($filePath)
+    {
+        $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+        $loader->load($filePath);
+    }
+
+    public function getPathsToInvalidFiles()
+    {
+        return array(array('nonvalid.yml'), array('nonvalid2.yml'), array('incomplete.yml'), array('nonvalidkeys.yml'), array('nonesense_resource_plus_path.yml'), array('nonesense_type_without_resource.yml'));
+    }
+
+    public function testLoadSpecialRouteName()
+    {
+        $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+        $routeCollection = $loader->load('special_route_name.yml');
+        $route = $routeCollection->get('#$péß^a|');
+
+        $this->assertInstanceOf('Symfony\Component\Routing\Route', $route);
+        $this->assertSame('/true', $route->getPath());
+    }
+
+    public function testLoadWithRoute()
+    {
+        $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+        $routeCollection = $loader->load('validpattern.yml');
+        $routes = $routeCollection->all();
+
+        $this->assertCount(2, $routes, 'Two routes are loaded');
+        $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
+
+        foreach ($routes as $route) {
+            $this->assertSame('/blog/{slug}', $route->getPath());
+            $this->assertSame('{locale}.example.com', $route->getHost());
+            $this->assertSame('MyBundle:Blog:show', $route->getDefault('_controller'));
+            $this->assertSame('\w+', $route->getRequirement('locale'));
+            $this->assertSame('RouteCompiler', $route->getOption('compiler_class'));
+            $this->assertEquals(array('GET', 'POST', 'PUT', 'OPTIONS'), $route->getMethods());
+            $this->assertEquals(array('https'), $route->getSchemes());
+        }
+    }
+
+    public function testLoadWithResource()
+    {
+        $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+        $routeCollection = $loader->load('validresource.yml');
+        $routes = $routeCollection->all();
+
+        $this->assertCount(2, $routes, 'Two routes are loaded');
+        $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
+
+        foreach ($routes as $route) {
+            $this->assertSame('/{foo}/blog/{slug}', $route->getPath());
+            $this->assertSame('123', $route->getDefault('foo'));
+            $this->assertSame('\d+', $route->getRequirement('foo'));
+            $this->assertSame('bar', $route->getOption('foo'));
+            $this->assertSame('', $route->getHost());
+        }
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/ApacheUrlMatcherTest.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/ApacheUrlMatcherTest.php
new file mode 100644 (file)
index 0000000..6550911
--- /dev/null
@@ -0,0 +1,137 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Matcher;
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\Matcher\ApacheUrlMatcher;
+
+class ApacheUrlMatcherTest extends \PHPUnit_Framework_TestCase
+{
+    protected $server;
+
+    protected function setUp()
+    {
+        $this->server = $_SERVER;
+    }
+
+    protected function tearDown()
+    {
+        $_SERVER = $this->server;
+    }
+
+    /**
+     * @dataProvider getMatchData
+     */
+    public function testMatch($name, $pathinfo, $server, $expect)
+    {
+        $collection = new RouteCollection();
+        $context = new RequestContext();
+        $matcher = new ApacheUrlMatcher($collection, $context);
+
+        $_SERVER = $server;
+
+        $result = $matcher->match($pathinfo, $server);
+        $this->assertSame(var_export($expect, true), var_export($result, true));
+    }
+
+    public function getMatchData()
+    {
+        return array(
+            array(
+                'Simple route',
+                '/hello/world',
+                array(
+                    '_ROUTING_route' => 'hello',
+                    '_ROUTING_param__controller' => 'AcmeBundle:Default:index',
+                    '_ROUTING_param_name' => 'world',
+                ),
+                array(
+                    '_controller' => 'AcmeBundle:Default:index',
+                    'name' => 'world',
+                    '_route' => 'hello',
+                ),
+            ),
+            array(
+                'Route with params and defaults',
+                '/hello/hugo',
+                array(
+                    '_ROUTING_route' => 'hello',
+                    '_ROUTING_param__controller' => 'AcmeBundle:Default:index',
+                    '_ROUTING_param_name' => 'hugo',
+                    '_ROUTING_default_name' => 'world',
+                ),
+                array(
+                    'name' => 'hugo',
+                    '_controller' => 'AcmeBundle:Default:index',
+                    '_route' => 'hello',
+                ),
+            ),
+            array(
+                'Route with defaults only',
+                '/hello',
+                array(
+                    '_ROUTING_route' => 'hello',
+                    '_ROUTING_param__controller' => 'AcmeBundle:Default:index',
+                    '_ROUTING_default_name' => 'world',
+                ),
+                array(
+                    'name' => 'world',
+                    '_controller' => 'AcmeBundle:Default:index',
+                    '_route' => 'hello',
+                ),
+            ),
+            array(
+                'REDIRECT_ envs',
+                '/hello/world',
+                array(
+                    'REDIRECT__ROUTING_route' => 'hello',
+                    'REDIRECT__ROUTING_param__controller' => 'AcmeBundle:Default:index',
+                    'REDIRECT__ROUTING_param_name' => 'world',
+                ),
+                array(
+                    '_controller' => 'AcmeBundle:Default:index',
+                    'name' => 'world',
+                    '_route' => 'hello',
+                ),
+            ),
+            array(
+                'REDIRECT_REDIRECT_ envs',
+                '/hello/world',
+                array(
+                    'REDIRECT_REDIRECT__ROUTING_route' => 'hello',
+                    'REDIRECT_REDIRECT__ROUTING_param__controller' => 'AcmeBundle:Default:index',
+                    'REDIRECT_REDIRECT__ROUTING_param_name' => 'world',
+                ),
+                array(
+                    '_controller' => 'AcmeBundle:Default:index',
+                    'name' => 'world',
+                    '_route' => 'hello',
+                ),
+            ),
+            array(
+                'REDIRECT_REDIRECT_ envs',
+                '/hello/world',
+                array(
+                    'REDIRECT_REDIRECT__ROUTING_route' => 'hello',
+                    'REDIRECT_REDIRECT__ROUTING_param__controller' => 'AcmeBundle:Default:index',
+                    'REDIRECT_REDIRECT__ROUTING_param_name' => 'world',
+                ),
+                array(
+                    '_controller' => 'AcmeBundle:Default:index',
+                    'name' => 'world',
+                    '_route' => 'hello',
+                ),
+            ),
+        );
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/Dumper/ApacheMatcherDumperTest.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/Dumper/ApacheMatcherDumperTest.php
new file mode 100644 (file)
index 0000000..72bee71
--- /dev/null
@@ -0,0 +1,196 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Matcher\Dumper;
+
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Matcher\Dumper\ApacheMatcherDumper;
+
+class ApacheMatcherDumperTest extends \PHPUnit_Framework_TestCase
+{
+    protected static $fixturesPath;
+
+    public static function setUpBeforeClass()
+    {
+        self::$fixturesPath = realpath(__DIR__.'/../../Fixtures/');
+    }
+
+    public function testDump()
+    {
+        $dumper = new ApacheMatcherDumper($this->getRouteCollection());
+
+        $this->assertStringEqualsFile(self::$fixturesPath.'/dumper/url_matcher1.apache', $dumper->dump(), '->dump() dumps basic routes to the correct apache format.');
+    }
+
+    /**
+     * @dataProvider provideEscapeFixtures
+     */
+    public function testEscapePattern($src, $dest, $char, $with, $message)
+    {
+        $r = new \ReflectionMethod(new ApacheMatcherDumper($this->getRouteCollection()), 'escape');
+        $r->setAccessible(true);
+        $this->assertEquals($dest, $r->invoke(null, $src, $char, $with), $message);
+    }
+
+    public function provideEscapeFixtures()
+    {
+        return array(
+            array('foo', 'foo', ' ', '-', 'Preserve string that should not be escaped'),
+            array('fo-o', 'fo-o', ' ', '-', 'Preserve string that should not be escaped'),
+            array('fo o', 'fo- o', ' ', '-', 'Escape special characters'),
+            array('fo-- o', 'fo--- o', ' ', '-', 'Escape special characters'),
+            array('fo- o', 'fo- o', ' ', '-', 'Do not escape already escaped string'),
+        );
+    }
+
+    public function testEscapeScriptName()
+    {
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/foo'));
+        $dumper = new ApacheMatcherDumper($collection);
+        $this->assertStringEqualsFile(self::$fixturesPath.'/dumper/url_matcher2.apache', $dumper->dump(array('script_name' => 'ap p_d\ ev.php')));
+    }
+
+    private function getRouteCollection()
+    {
+        $collection = new RouteCollection();
+
+        // defaults and requirements
+        $collection->add('foo', new Route(
+            '/foo/{bar}',
+            array('def' => 'test'),
+            array('bar' => 'baz|symfony')
+        ));
+        // defaults parameters in pattern
+        $collection->add('foobar', new Route(
+            '/foo/{bar}',
+            array('bar' => 'toto')
+        ));
+        // method requirement
+        $collection->add('bar', new Route(
+            '/bar/{foo}',
+            array(),
+            array('_method' => 'GET|head')
+        ));
+        // method requirement (again)
+        $collection->add('baragain', new Route(
+            '/baragain/{foo}',
+            array(),
+            array('_method' => 'get|post')
+        ));
+        // simple
+        $collection->add('baz', new Route(
+            '/test/baz'
+        ));
+        // simple with extension
+        $collection->add('baz2', new Route(
+            '/test/baz.html'
+        ));
+        // trailing slash
+        $collection->add('baz3', new Route(
+            '/test/baz3/'
+        ));
+        // trailing slash with variable
+        $collection->add('baz4', new Route(
+            '/test/{foo}/'
+        ));
+        // trailing slash and safe method
+        $collection->add('baz5', new Route(
+            '/test/{foo}/',
+            array(),
+            array('_method' => 'get')
+        ));
+        // trailing slash and unsafe method
+        $collection->add('baz5unsafe', new Route(
+            '/testunsafe/{foo}/',
+            array(),
+            array('_method' => 'post')
+        ));
+        // complex
+        $collection->add('baz6', new Route(
+            '/test/baz',
+            array('foo' => 'bar baz')
+        ));
+        // space in path
+        $collection->add('baz7', new Route(
+            '/te st/baz'
+        ));
+        // space preceded with \ in path
+        $collection->add('baz8', new Route(
+            '/te\\ st/baz'
+        ));
+        // space preceded with \ in requirement
+        $collection->add('baz9', new Route(
+            '/test/{baz}',
+            array(),
+            array(
+                'baz' => 'te\\\\ st',
+            )
+        ));
+
+        $collection1 = new RouteCollection();
+
+        $route1 = new Route('/route1', array(), array(), array(), 'a.example.com');
+        $collection1->add('route1', $route1);
+
+        $collection2 = new RouteCollection();
+
+        $route2 = new Route('/route2', array(), array(), array(), 'a.example.com');
+        $collection2->add('route2', $route2);
+
+        $route3 = new Route('/route3', array(), array(), array(), 'b.example.com');
+        $collection2->add('route3', $route3);
+
+        $collection2->addPrefix('/c2');
+        $collection1->addCollection($collection2);
+
+        $route4 = new Route('/route4', array(), array(), array(), 'a.example.com');
+        $collection1->add('route4', $route4);
+
+        $route5 = new Route('/route5', array(), array(), array(), 'c.example.com');
+        $collection1->add('route5', $route5);
+
+        $route6 = new Route('/route6', array(), array(), array(), null);
+        $collection1->add('route6', $route6);
+
+        $collection->addCollection($collection1);
+
+        // host and variables
+
+        $collection1 = new RouteCollection();
+
+        $route11 = new Route('/route11', array(), array(), array(), '{var1}.example.com');
+        $collection1->add('route11', $route11);
+
+        $route12 = new Route('/route12', array('var1' => 'val'), array(), array(), '{var1}.example.com');
+        $collection1->add('route12', $route12);
+
+        $route13 = new Route('/route13/{name}', array(), array(), array(), '{var1}.example.com');
+        $collection1->add('route13', $route13);
+
+        $route14 = new Route('/route14/{name}', array('var1' => 'val'), array(), array(), '{var1}.example.com');
+        $collection1->add('route14', $route14);
+
+        $route15 = new Route('/route15/{name}', array(), array(), array(), 'c.example.com');
+        $collection1->add('route15', $route15);
+
+        $route16 = new Route('/route16/{name}', array('var1' => 'val'), array(), array(), null);
+        $collection1->add('route16', $route16);
+
+        $route17 = new Route('/route17', array(), array(), array(), null);
+        $collection1->add('route17', $route17);
+
+        $collection->addCollection($collection1);
+
+        return $collection;
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/Dumper/DumperCollectionTest.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/Dumper/DumperCollectionTest.php
new file mode 100644 (file)
index 0000000..54b3772
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Test\Matcher\Dumper;
+
+use Symfony\Component\Routing\Matcher\Dumper\DumperCollection;
+
+class DumperCollectionTest extends \PHPUnit_Framework_TestCase
+{
+    public function testGetRoot()
+    {
+        $a = new DumperCollection();
+
+        $b = new DumperCollection();
+        $a->add($b);
+
+        $c = new DumperCollection();
+        $b->add($c);
+
+        $d = new DumperCollection();
+        $c->add($d);
+
+        $this->assertSame($a, $c->getRoot());
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/Dumper/DumperPrefixCollectionTest.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/Dumper/DumperPrefixCollectionTest.php
new file mode 100644 (file)
index 0000000..7b4565c
--- /dev/null
@@ -0,0 +1,123 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Matcher\Dumper;
+
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\Matcher\Dumper\DumperPrefixCollection;
+use Symfony\Component\Routing\Matcher\Dumper\DumperRoute;
+use Symfony\Component\Routing\Matcher\Dumper\DumperCollection;
+
+class DumperPrefixCollectionTest extends \PHPUnit_Framework_TestCase
+{
+    public function testAddPrefixRoute()
+    {
+        $coll = new DumperPrefixCollection;
+        $coll->setPrefix('');
+
+        $route = new DumperRoute('bar', new Route('/foo/bar'));
+        $coll = $coll->addPrefixRoute($route);
+
+        $route = new DumperRoute('bar2', new Route('/foo/bar'));
+        $coll = $coll->addPrefixRoute($route);
+
+        $route = new DumperRoute('qux', new Route('/foo/qux'));
+        $coll = $coll->addPrefixRoute($route);
+
+        $route = new DumperRoute('bar3', new Route('/foo/bar'));
+        $coll = $coll->addPrefixRoute($route);
+
+        $route = new DumperRoute('bar4', new Route(''));
+        $result = $coll->addPrefixRoute($route);
+
+        $expect = <<<'EOF'
+            |-coll /
+            | |-coll /f
+            | | |-coll /fo
+            | | | |-coll /foo
+            | | | | |-coll /foo/
+            | | | | | |-coll /foo/b
+            | | | | | | |-coll /foo/ba
+            | | | | | | | |-coll /foo/bar
+            | | | | | | | | |-route bar /foo/bar
+            | | | | | | | | |-route bar2 /foo/bar
+            | | | | | |-coll /foo/q
+            | | | | | | |-coll /foo/qu
+            | | | | | | | |-coll /foo/qux
+            | | | | | | | | |-route qux /foo/qux
+            | | | | | |-coll /foo/b
+            | | | | | | |-coll /foo/ba
+            | | | | | | | |-coll /foo/bar
+            | | | | | | | | |-route bar3 /foo/bar
+            | |-route bar4 /
+
+EOF;
+
+        $this->assertSame($expect, $this->collectionToString($result->getRoot(), '            '));
+    }
+
+    public function testMergeSlashNodes()
+    {
+        $coll = new DumperPrefixCollection;
+        $coll->setPrefix('');
+
+        $route = new DumperRoute('bar', new Route('/foo/bar'));
+        $coll = $coll->addPrefixRoute($route);
+
+        $route = new DumperRoute('bar2', new Route('/foo/bar'));
+        $coll = $coll->addPrefixRoute($route);
+
+        $route = new DumperRoute('qux', new Route('/foo/qux'));
+        $coll = $coll->addPrefixRoute($route);
+
+        $route = new DumperRoute('bar3', new Route('/foo/bar'));
+        $result = $coll->addPrefixRoute($route);
+
+        $result->getRoot()->mergeSlashNodes();
+
+        $expect = <<<'EOF'
+            |-coll /f
+            | |-coll /fo
+            | | |-coll /foo
+            | | | |-coll /foo/b
+            | | | | |-coll /foo/ba
+            | | | | | |-coll /foo/bar
+            | | | | | | |-route bar /foo/bar
+            | | | | | | |-route bar2 /foo/bar
+            | | | |-coll /foo/q
+            | | | | |-coll /foo/qu
+            | | | | | |-coll /foo/qux
+            | | | | | | |-route qux /foo/qux
+            | | | |-coll /foo/b
+            | | | | |-coll /foo/ba
+            | | | | | |-coll /foo/bar
+            | | | | | | |-route bar3 /foo/bar
+
+EOF;
+
+        $this->assertSame($expect, $this->collectionToString($result->getRoot(), '            '));
+    }
+
+    private function collectionToString(DumperCollection $collection, $prefix)
+    {
+        $string = '';
+        foreach ($collection as $route) {
+            if ($route instanceof DumperCollection) {
+                $string .= sprintf("%s|-coll %s\n", $prefix, $route->getPrefix());
+                $string .= $this->collectionToString($route, $prefix.'| ');
+            } else {
+                $string .= sprintf("%s|-route %s %s\n", $prefix, $route->getName(), $route->getRoute()->getPath());
+            }
+        }
+
+        return $string;
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php
new file mode 100644 (file)
index 0000000..542ede8
--- /dev/null
@@ -0,0 +1,261 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Matcher\Dumper;
+
+use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+class PhpMatcherDumperTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @expectedException \LogicException
+     */
+    public function testDumpWhenSchemeIsUsedWithoutAProperDumper()
+    {
+        $collection = new RouteCollection();
+        $collection->add('secure', new Route(
+            '/secure',
+            array(),
+            array('_scheme' => 'https')
+        ));
+        $dumper = new PhpMatcherDumper($collection);
+        $dumper->dump();
+    }
+
+    /**
+     * @dataProvider getRouteCollections
+     */
+    public function testDump(RouteCollection $collection, $fixture, $options = array())
+    {
+        $basePath = __DIR__.'/../../Fixtures/dumper/';
+
+        $dumper = new PhpMatcherDumper($collection);
+        $this->assertStringEqualsFile($basePath.$fixture, $dumper->dump($options), '->dump() correctly dumps routes as optimized PHP code.');
+    }
+
+    public function getRouteCollections()
+    {
+        /* test case 1 */
+
+        $collection = new RouteCollection();
+
+        $collection->add('overridden', new Route('/overridden'));
+
+        // defaults and requirements
+        $collection->add('foo', new Route(
+            '/foo/{bar}',
+            array('def' => 'test'),
+            array('bar' => 'baz|symfony')
+        ));
+        // method requirement
+        $collection->add('bar', new Route(
+            '/bar/{foo}',
+            array(),
+            array('_method' => 'GET|head')
+        ));
+        // GET method requirement automatically adds HEAD as valid
+        $collection->add('barhead', new Route(
+            '/barhead/{foo}',
+            array(),
+            array('_method' => 'GET')
+        ));
+        // simple
+        $collection->add('baz', new Route(
+            '/test/baz'
+        ));
+        // simple with extension
+        $collection->add('baz2', new Route(
+            '/test/baz.html'
+        ));
+        // trailing slash
+        $collection->add('baz3', new Route(
+            '/test/baz3/'
+        ));
+        // trailing slash with variable
+        $collection->add('baz4', new Route(
+            '/test/{foo}/'
+        ));
+        // trailing slash and method
+        $collection->add('baz5', new Route(
+            '/test/{foo}/',
+            array(),
+            array('_method' => 'post')
+        ));
+        // complex name
+        $collection->add('baz.baz6', new Route(
+            '/test/{foo}/',
+            array(),
+            array('_method' => 'put')
+        ));
+        // defaults without variable
+        $collection->add('foofoo', new Route(
+            '/foofoo',
+            array('def' => 'test')
+        ));
+        // pattern with quotes
+        $collection->add('quoter', new Route(
+            '/{quoter}',
+            array(),
+            array('quoter' => '[\']+')
+        ));
+        // space in pattern
+        $collection->add('space', new Route(
+            '/spa ce'
+        ));
+
+        // prefixes
+        $collection1 = new RouteCollection();
+        $collection1->add('overridden', new Route('/overridden1'));
+        $collection1->add('foo1', new Route('/{foo}'));
+        $collection1->add('bar1', new Route('/{bar}'));
+        $collection1->addPrefix('/b\'b');
+        $collection2 = new RouteCollection();
+        $collection2->addCollection($collection1);
+        $collection2->add('overridden', new Route('/{var}', array(), array('var' => '.*')));
+        $collection1 = new RouteCollection();
+        $collection1->add('foo2', new Route('/{foo1}'));
+        $collection1->add('bar2', new Route('/{bar1}'));
+        $collection1->addPrefix('/b\'b');
+        $collection2->addCollection($collection1);
+        $collection2->addPrefix('/a');
+        $collection->addCollection($collection2);
+
+        // overridden through addCollection() and multiple sub-collections with no own prefix
+        $collection1 = new RouteCollection();
+        $collection1->add('overridden2', new Route('/old'));
+        $collection1->add('helloWorld', new Route('/hello/{who}', array('who' => 'World!')));
+        $collection2 = new RouteCollection();
+        $collection3 = new RouteCollection();
+        $collection3->add('overridden2', new Route('/new'));
+        $collection3->add('hey', new Route('/hey/'));
+        $collection2->addCollection($collection3);
+        $collection1->addCollection($collection2);
+        $collection1->addPrefix('/multi');
+        $collection->addCollection($collection1);
+
+        // "dynamic" prefix
+        $collection1 = new RouteCollection();
+        $collection1->add('foo3', new Route('/{foo}'));
+        $collection1->add('bar3', new Route('/{bar}'));
+        $collection1->addPrefix('/b');
+        $collection1->addPrefix('{_locale}');
+        $collection->addCollection($collection1);
+
+        // route between collections
+        $collection->add('ababa', new Route('/ababa'));
+
+        // collection with static prefix but only one route
+        $collection1 = new RouteCollection();
+        $collection1->add('foo4', new Route('/{foo}'));
+        $collection1->addPrefix('/aba');
+        $collection->addCollection($collection1);
+
+        // prefix and host
+
+        $collection1 = new RouteCollection();
+
+        $route1 = new Route('/route1', array(), array(), array(), 'a.example.com');
+        $collection1->add('route1', $route1);
+
+        $collection2 = new RouteCollection();
+
+        $route2 = new Route('/c2/route2', array(), array(), array(), 'a.example.com');
+        $collection1->add('route2', $route2);
+
+        $route3 = new Route('/c2/route3', array(), array(), array(), 'b.example.com');
+        $collection1->add('route3', $route3);
+
+        $route4 = new Route('/route4', array(), array(), array(), 'a.example.com');
+        $collection1->add('route4', $route4);
+
+        $route5 = new Route('/route5', array(), array(), array(), 'c.example.com');
+        $collection1->add('route5', $route5);
+
+        $route6 = new Route('/route6', array(), array(), array(), null);
+        $collection1->add('route6', $route6);
+
+        $collection->addCollection($collection1);
+
+        // host and variables
+
+        $collection1 = new RouteCollection();
+
+        $route11 = new Route('/route11', array(), array(), array(), '{var1}.example.com');
+        $collection1->add('route11', $route11);
+
+        $route12 = new Route('/route12', array('var1' => 'val'), array(), array(), '{var1}.example.com');
+        $collection1->add('route12', $route12);
+
+        $route13 = new Route('/route13/{name}', array(), array(), array(), '{var1}.example.com');
+        $collection1->add('route13', $route13);
+
+        $route14 = new Route('/route14/{name}', array('var1' => 'val'), array(), array(), '{var1}.example.com');
+        $collection1->add('route14', $route14);
+
+        $route15 = new Route('/route15/{name}', array(), array(), array(), 'c.example.com');
+        $collection1->add('route15', $route15);
+
+        $route16 = new Route('/route16/{name}', array('var1' => 'val'), array(), array(), null);
+        $collection1->add('route16', $route16);
+
+        $route17 = new Route('/route17', array(), array(), array(), null);
+        $collection1->add('route17', $route17);
+
+        $collection->addCollection($collection1);
+
+        // multiple sub-collections with a single route and a prefix each
+        $collection1 = new RouteCollection();
+        $collection1->add('a', new Route('/a...'));
+        $collection2 = new RouteCollection();
+        $collection2->add('b', new Route('/{var}'));
+        $collection3 = new RouteCollection();
+        $collection3->add('c', new Route('/{var}'));
+        $collection3->addPrefix('/c');
+        $collection2->addCollection($collection3);
+        $collection2->addPrefix('/b');
+        $collection1->addCollection($collection2);
+        $collection1->addPrefix('/a');
+        $collection->addCollection($collection1);
+
+        /* test case 2 */
+
+        $redirectCollection = clone $collection;
+
+        // force HTTPS redirection
+        $redirectCollection->add('secure', new Route(
+            '/secure',
+            array(),
+            array('_scheme' => 'https')
+        ));
+
+        // force HTTP redirection
+        $redirectCollection->add('nonsecure', new Route(
+            '/nonsecure',
+            array(),
+            array('_scheme' => 'http')
+        ));
+
+        /* test case 3 */
+
+        $rootprefixCollection = new RouteCollection();
+        $rootprefixCollection->add('static', new Route('/test'));
+        $rootprefixCollection->add('dynamic', new Route('/{var}'));
+        $rootprefixCollection->addPrefix('rootprefix');
+
+        return array(
+           array($collection, 'url_matcher1.php', array()),
+           array($redirectCollection, 'url_matcher2.php', array('base_class' => 'Symfony\Component\Routing\Tests\Fixtures\RedirectableUrlMatcher')),
+           array($rootprefixCollection, 'url_matcher3.php', array())
+        );
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php
new file mode 100644 (file)
index 0000000..2ad4fc8
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Matcher;
+
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\RequestContext;
+
+class RedirectableUrlMatcherTest extends \PHPUnit_Framework_TestCase
+{
+    public function testRedirectWhenNoSlash()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo', new Route('/foo/'));
+
+        $matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, new RequestContext()));
+        $matcher->expects($this->once())->method('redirect');
+        $matcher->match('/foo');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
+     */
+    public function testRedirectWhenNoSlashForNonSafeMethod()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo', new Route('/foo/'));
+
+        $context = new RequestContext();
+        $context->setMethod('POST');
+        $matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, $context));
+        $matcher->match('/foo');
+    }
+
+    public function testSchemeRedirect()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo', new Route('/foo', array(), array('_scheme' => 'https')));
+
+        $matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, new RequestContext()));
+        $matcher
+            ->expects($this->once())
+            ->method('redirect')
+            ->with('/foo', 'foo', 'https')
+            ->will($this->returnValue(array('_route' => 'foo')))
+        ;
+        $matcher->match('/foo');
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/TraceableUrlMatcherTest.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/TraceableUrlMatcherTest.php
new file mode 100644 (file)
index 0000000..86d8d95
--- /dev/null
@@ -0,0 +1,66 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Matcher;
+
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\Matcher\TraceableUrlMatcher;
+
+class TraceableUrlMatcherTest extends \PHPUnit_Framework_TestCase
+{
+    public function test()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo', new Route('/foo', array(), array('_method' => 'POST')));
+        $coll->add('bar', new Route('/bar/{id}', array(), array('id' => '\d+')));
+        $coll->add('bar1', new Route('/bar/{name}', array(), array('id' => '\w+', '_method' => 'POST')));
+        $coll->add('bar2', new Route('/foo', array(), array(), array(), 'baz'));
+        $coll->add('bar3', new Route('/foo1', array(), array(), array(), 'baz'));
+
+        $context = new RequestContext();
+        $context->setHost('baz');
+
+        $matcher = new TraceableUrlMatcher($coll, $context);
+        $traces = $matcher->getTraces('/babar');
+        $this->assertEquals(array(0, 0, 0, 0, 0), $this->getLevels($traces));
+
+        $traces = $matcher->getTraces('/foo');
+        $this->assertEquals(array(1, 0, 0, 2), $this->getLevels($traces));
+
+        $traces = $matcher->getTraces('/bar/12');
+        $this->assertEquals(array(0, 2), $this->getLevels($traces));
+
+        $traces = $matcher->getTraces('/bar/dd');
+        $this->assertEquals(array(0, 1, 1, 0, 0), $this->getLevels($traces));
+
+        $traces = $matcher->getTraces('/foo1');
+        $this->assertEquals(array(0, 0, 0, 0, 2), $this->getLevels($traces));
+
+        $context->setMethod('POST');
+        $traces = $matcher->getTraces('/foo');
+        $this->assertEquals(array(2), $this->getLevels($traces));
+
+        $traces = $matcher->getTraces('/bar/dd');
+        $this->assertEquals(array(0, 1, 2), $this->getLevels($traces));
+    }
+
+    public function getLevels($traces)
+    {
+        $levels = array();
+        foreach ($traces as $trace) {
+            $levels[] = $trace['level'];
+        }
+
+        return $levels;
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php
new file mode 100644 (file)
index 0000000..8a1428f
--- /dev/null
@@ -0,0 +1,383 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Matcher;
+
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\Matcher\UrlMatcher;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\RequestContext;
+
+class UrlMatcherTest extends \PHPUnit_Framework_TestCase
+{
+    public function testNoMethodSoAllowed()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo', new Route('/foo'));
+
+        $matcher = new UrlMatcher($coll, new RequestContext());
+        $matcher->match('/foo');
+    }
+
+    public function testMethodNotAllowed()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo', new Route('/foo', array(), array('_method' => 'post')));
+
+        $matcher = new UrlMatcher($coll, new RequestContext());
+
+        try {
+            $matcher->match('/foo');
+            $this->fail();
+        } catch (MethodNotAllowedException $e) {
+            $this->assertEquals(array('POST'), $e->getAllowedMethods());
+        }
+    }
+
+    public function testHeadAllowedWhenRequirementContainsGet()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo', new Route('/foo', array(), array('_method' => 'get')));
+
+        $matcher = new UrlMatcher($coll, new RequestContext('', 'head'));
+        $matcher->match('/foo');
+    }
+
+    public function testMethodNotAllowedAggregatesAllowedMethods()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo1', new Route('/foo', array(), array('_method' => 'post')));
+        $coll->add('foo2', new Route('/foo', array(), array('_method' => 'put|delete')));
+
+        $matcher = new UrlMatcher($coll, new RequestContext());
+
+        try {
+            $matcher->match('/foo');
+            $this->fail();
+        } catch (MethodNotAllowedException $e) {
+            $this->assertEquals(array('POST', 'PUT', 'DELETE'), $e->getAllowedMethods());
+        }
+    }
+
+    public function testMatch()
+    {
+        // test the patterns are matched and parameters are returned
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/foo/{bar}'));
+        $matcher = new UrlMatcher($collection, new RequestContext());
+        try {
+            $matcher->match('/no-match');
+            $this->fail();
+        } catch (ResourceNotFoundException $e) {}
+        $this->assertEquals(array('_route' => 'foo', 'bar' => 'baz'), $matcher->match('/foo/baz'));
+
+        // test that defaults are merged
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/foo/{bar}', array('def' => 'test')));
+        $matcher = new UrlMatcher($collection, new RequestContext());
+        $this->assertEquals(array('_route' => 'foo', 'bar' => 'baz', 'def' => 'test'), $matcher->match('/foo/baz'));
+
+        // test that route "method" is ignored if no method is given in the context
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/foo', array(), array('_method' => 'GET|head')));
+        $matcher = new UrlMatcher($collection, new RequestContext());
+        $this->assertInternalType('array', $matcher->match('/foo'));
+
+        // route does not match with POST method context
+        $matcher = new UrlMatcher($collection, new RequestContext('', 'post'));
+        try {
+            $matcher->match('/foo');
+            $this->fail();
+        } catch (MethodNotAllowedException $e) {}
+
+        // route does match with GET or HEAD method context
+        $matcher = new UrlMatcher($collection, new RequestContext());
+        $this->assertInternalType('array', $matcher->match('/foo'));
+        $matcher = new UrlMatcher($collection, new RequestContext('', 'head'));
+        $this->assertInternalType('array', $matcher->match('/foo'));
+
+        // route with an optional variable as the first segment
+        $collection = new RouteCollection();
+        $collection->add('bar', new Route('/{bar}/foo', array('bar' => 'bar'), array('bar' => 'foo|bar')));
+        $matcher = new UrlMatcher($collection, new RequestContext());
+        $this->assertEquals(array('_route' => 'bar', 'bar' => 'bar'), $matcher->match('/bar/foo'));
+        $this->assertEquals(array('_route' => 'bar', 'bar' => 'foo'), $matcher->match('/foo/foo'));
+
+        $collection = new RouteCollection();
+        $collection->add('bar', new Route('/{bar}', array('bar' => 'bar'), array('bar' => 'foo|bar')));
+        $matcher = new UrlMatcher($collection, new RequestContext());
+        $this->assertEquals(array('_route' => 'bar', 'bar' => 'foo'), $matcher->match('/foo'));
+        $this->assertEquals(array('_route' => 'bar', 'bar' => 'bar'), $matcher->match('/'));
+
+        // route with only optional variables
+        $collection = new RouteCollection();
+        $collection->add('bar', new Route('/{foo}/{bar}', array('foo' => 'foo', 'bar' => 'bar'), array()));
+        $matcher = new UrlMatcher($collection, new RequestContext());
+        $this->assertEquals(array('_route' => 'bar', 'foo' => 'foo', 'bar' => 'bar'), $matcher->match('/'));
+        $this->assertEquals(array('_route' => 'bar', 'foo' => 'a', 'bar' => 'bar'), $matcher->match('/a'));
+        $this->assertEquals(array('_route' => 'bar', 'foo' => 'a', 'bar' => 'b'), $matcher->match('/a/b'));
+    }
+
+    public function testMatchWithPrefixes()
+    {
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/{foo}'));
+        $collection->addPrefix('/b');
+        $collection->addPrefix('/a');
+
+        $matcher = new UrlMatcher($collection, new RequestContext());
+        $this->assertEquals(array('_route' => 'foo', 'foo' => 'foo'), $matcher->match('/a/b/foo'));
+    }
+
+    public function testMatchWithDynamicPrefix()
+    {
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/{foo}'));
+        $collection->addPrefix('/b');
+        $collection->addPrefix('/{_locale}');
+
+        $matcher = new UrlMatcher($collection, new RequestContext());
+        $this->assertEquals(array('_locale' => 'fr', '_route' => 'foo', 'foo' => 'foo'), $matcher->match('/fr/b/foo'));
+    }
+
+    public function testMatchSpecialRouteName()
+    {
+        $collection = new RouteCollection();
+        $collection->add('$péß^a|', new Route('/bar'));
+
+        $matcher = new UrlMatcher($collection, new RequestContext());
+        $this->assertEquals(array('_route' => '$péß^a|'), $matcher->match('/bar'));
+    }
+
+    public function testMatchNonAlpha()
+    {
+        $collection = new RouteCollection();
+        $chars = '!"$%éà &\'()*+,./:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ\\[]^_`abcdefghijklmnopqrstuvwxyz{|}~-';
+        $collection->add('foo', new Route('/{foo}/bar', array(), array('foo' => '['.preg_quote($chars).']+')));
+
+        $matcher = new UrlMatcher($collection, new RequestContext());
+        $this->assertEquals(array('_route' => 'foo', 'foo' => $chars), $matcher->match('/'.rawurlencode($chars).'/bar'));
+        $this->assertEquals(array('_route' => 'foo', 'foo' => $chars), $matcher->match('/'.strtr($chars, array('%' => '%25')).'/bar'));
+    }
+
+    public function testMatchWithDotMetacharacterInRequirements()
+    {
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/{foo}/bar', array(), array('foo' => '.+')));
+
+        $matcher = new UrlMatcher($collection, new RequestContext());
+        $this->assertEquals(array('_route' => 'foo', 'foo' => "\n"), $matcher->match('/'.urlencode("\n").'/bar'), 'linefeed character is matched');
+    }
+
+    public function testMatchOverriddenRoute()
+    {
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/foo'));
+
+        $collection1 = new RouteCollection();
+        $collection1->add('foo', new Route('/foo1'));
+
+        $collection->addCollection($collection1);
+
+        $matcher = new UrlMatcher($collection, new RequestContext());
+
+        $this->assertEquals(array('_route' => 'foo'), $matcher->match('/foo1'));
+        $this->setExpectedException('Symfony\Component\Routing\Exception\ResourceNotFoundException');
+        $this->assertEquals(array(), $matcher->match('/foo'));
+    }
+
+    public function testMatchRegression()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo', new Route('/foo/{foo}'));
+        $coll->add('bar', new Route('/foo/bar/{foo}'));
+
+        $matcher = new UrlMatcher($coll, new RequestContext());
+        $this->assertEquals(array('foo' => 'bar', '_route' => 'bar'), $matcher->match('/foo/bar/bar'));
+
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/{bar}'));
+        $matcher = new UrlMatcher($collection, new RequestContext());
+        try {
+            $matcher->match('/');
+            $this->fail();
+        } catch (ResourceNotFoundException $e) {
+        }
+    }
+
+    public function testDefaultRequirementForOptionalVariables()
+    {
+        $coll = new RouteCollection();
+        $coll->add('test', new Route('/{page}.{_format}', array('page' => 'index', '_format' => 'html')));
+
+        $matcher = new UrlMatcher($coll, new RequestContext());
+        $this->assertEquals(array('page' => 'my-page', '_format' => 'xml', '_route' => 'test'), $matcher->match('/my-page.xml'));
+    }
+
+    public function testMatchingIsEager()
+    {
+        $coll = new RouteCollection();
+        $coll->add('test', new Route('/{foo}-{bar}-', array(), array('foo' => '.+', 'bar' => '.+')));
+
+        $matcher = new UrlMatcher($coll, new RequestContext());
+        $this->assertEquals(array('foo' => 'text1-text2-text3', 'bar' => 'text4', '_route' => 'test'), $matcher->match('/text1-text2-text3-text4-'));
+    }
+
+    public function testAdjacentVariables()
+    {
+        $coll = new RouteCollection();
+        $coll->add('test', new Route('/{w}{x}{y}{z}.{_format}', array('z' => 'default-z', '_format' => 'html'), array('y' => 'y|Y')));
+
+        $matcher = new UrlMatcher($coll, new RequestContext());
+        // 'w' eagerly matches as much as possible and the other variables match the remaining chars.
+        // This also shows that the variables w-z must all exclude the separating char (the dot '.' in this case) by default requirement.
+        // Otherwise they would also consume '.xml' and _format would never match as it's an optional variable.
+        $this->assertEquals(array('w' => 'wwwww', 'x' => 'x', 'y' => 'Y', 'z' => 'Z','_format' => 'xml', '_route' => 'test'), $matcher->match('/wwwwwxYZ.xml'));
+        // As 'y' has custom requirement and can only be of value 'y|Y', it will leave  'ZZZ' to variable z.
+        // So with carefully chosen requirements adjacent variables, can be useful.
+        $this->assertEquals(array('w' => 'wwwww', 'x' => 'x', 'y' => 'y', 'z' => 'ZZZ','_format' => 'html', '_route' => 'test'), $matcher->match('/wwwwwxyZZZ'));
+        // z and _format are optional.
+        $this->assertEquals(array('w' => 'wwwww', 'x' => 'x', 'y' => 'y', 'z' => 'default-z','_format' => 'html', '_route' => 'test'), $matcher->match('/wwwwwxy'));
+
+        $this->setExpectedException('Symfony\Component\Routing\Exception\ResourceNotFoundException');
+        $matcher->match('/wxy.html');
+    }
+
+    public function testOptionalVariableWithNoRealSeparator()
+    {
+        $coll = new RouteCollection();
+        $coll->add('test', new Route('/get{what}', array('what' => 'All')));
+        $matcher = new UrlMatcher($coll, new RequestContext());
+
+        $this->assertEquals(array('what' => 'All', '_route' => 'test'), $matcher->match('/get'));
+        $this->assertEquals(array('what' => 'Sites', '_route' => 'test'), $matcher->match('/getSites'));
+
+        // Usually the character in front of an optional parameter can be left out, e.g. with pattern '/get/{what}' just '/get' would match.
+        // But here the 't' in 'get' is not a separating character, so it makes no sense to match without it.
+        $this->setExpectedException('Symfony\Component\Routing\Exception\ResourceNotFoundException');
+        $matcher->match('/ge');
+    }
+
+    public function testRequiredVariableWithNoRealSeparator()
+    {
+        $coll = new RouteCollection();
+        $coll->add('test', new Route('/get{what}Suffix'));
+        $matcher = new UrlMatcher($coll, new RequestContext());
+
+        $this->assertEquals(array('what' => 'Sites', '_route' => 'test'), $matcher->match('/getSitesSuffix'));
+    }
+
+    public function testDefaultRequirementOfVariable()
+    {
+        $coll = new RouteCollection();
+        $coll->add('test', new Route('/{page}.{_format}'));
+        $matcher = new UrlMatcher($coll, new RequestContext());
+
+        $this->assertEquals(array('page' => 'index', '_format' => 'mobile.html', '_route' => 'test'), $matcher->match('/index.mobile.html'));
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
+     */
+    public function testDefaultRequirementOfVariableDisallowsSlash()
+    {
+        $coll = new RouteCollection();
+        $coll->add('test', new Route('/{page}.{_format}'));
+        $matcher = new UrlMatcher($coll, new RequestContext());
+
+        $matcher->match('/index.sl/ash');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
+     */
+    public function testDefaultRequirementOfVariableDisallowsNextSeparator()
+    {
+        $coll = new RouteCollection();
+        $coll->add('test', new Route('/{page}.{_format}', array(), array('_format' => 'html|xml')));
+        $matcher = new UrlMatcher($coll, new RequestContext());
+
+        $matcher->match('/do.t.html');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
+     */
+    public function testSchemeRequirement()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo', new Route('/foo', array(), array('_scheme' => 'https')));
+        $matcher = new UrlMatcher($coll, new RequestContext());
+        $matcher->match('/foo');
+    }
+
+    public function testDecodeOnce()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo', new Route('/foo/{foo}'));
+
+        $matcher = new UrlMatcher($coll, new RequestContext());
+        $this->assertEquals(array('foo' => 'bar%23', '_route' => 'foo'), $matcher->match('/foo/bar%2523'));
+    }
+
+    public function testCannotRelyOnPrefix()
+    {
+        $coll = new RouteCollection();
+
+        $subColl = new RouteCollection();
+        $subColl->add('bar', new Route('/bar'));
+        $subColl->addPrefix('/prefix');
+        // overwrite the pattern, so the prefix is not valid anymore for this route in the collection
+        $subColl->get('bar')->setPattern('/new');
+
+        $coll->addCollection($subColl);
+
+        $matcher = new UrlMatcher($coll, new RequestContext());
+        $this->assertEquals(array('_route' => 'bar'), $matcher->match('/new'));
+    }
+
+    public function testWithHost()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo', new Route('/foo/{foo}', array(), array(), array(), '{locale}.example.com'));
+
+        $matcher = new UrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com'));
+        $this->assertEquals(array('foo' => 'bar', '_route' => 'foo', 'locale' => 'en'), $matcher->match('/foo/bar'));
+    }
+
+    public function testWithHostOnRouteCollection()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo', new Route('/foo/{foo}'));
+        $coll->add('bar', new Route('/bar/{foo}', array(), array(), array(), '{locale}.example.net'));
+        $coll->setHost('{locale}.example.com');
+
+        $matcher = new UrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com'));
+        $this->assertEquals(array('foo' => 'bar', '_route' => 'foo', 'locale' => 'en'), $matcher->match('/foo/bar'));
+
+        $matcher = new UrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com'));
+        $this->assertEquals(array('foo' => 'bar', '_route' => 'bar', 'locale' => 'en'), $matcher->match('/bar/bar'));
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
+     */
+    public function testWithOutHostHostDoesNotMatch()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo', new Route('/foo/{foo}', array(), array(), array(), '{locale}.example.com'));
+
+        $matcher = new UrlMatcher($coll, new RequestContext('', 'GET', 'example.com'));
+        $matcher->match('/foo/bar');
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/RouteCollectionTest.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/RouteCollectionTest.php
new file mode 100644 (file)
index 0000000..3d78adf
--- /dev/null
@@ -0,0 +1,255 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests;
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Config\Resource\FileResource;
+
+class RouteCollectionTest extends \PHPUnit_Framework_TestCase
+{
+    public function testRoute()
+    {
+        $collection = new RouteCollection();
+        $route = new Route('/foo');
+        $collection->add('foo', $route);
+        $this->assertEquals(array('foo' => $route), $collection->all(), '->add() adds a route');
+        $this->assertEquals($route, $collection->get('foo'), '->get() returns a route by name');
+        $this->assertNull($collection->get('bar'), '->get() returns null if a route does not exist');
+    }
+
+    public function testOverriddenRoute()
+    {
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/foo'));
+        $collection->add('foo', new Route('/foo1'));
+
+        $this->assertEquals('/foo1', $collection->get('foo')->getPath());
+    }
+
+    public function testDeepOverriddenRoute()
+    {
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/foo'));
+
+        $collection1 = new RouteCollection();
+        $collection1->add('foo', new Route('/foo1'));
+
+        $collection2 = new RouteCollection();
+        $collection2->add('foo', new Route('/foo2'));
+
+        $collection1->addCollection($collection2);
+        $collection->addCollection($collection1);
+
+        $this->assertEquals('/foo2', $collection1->get('foo')->getPath());
+        $this->assertEquals('/foo2', $collection->get('foo')->getPath());
+    }
+
+    public function testIterator()
+    {
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/foo'));
+
+        $collection1 = new RouteCollection();
+        $collection1->add('bar', $bar = new Route('/bar'));
+        $collection1->add('foo', $foo = new Route('/foo-new'));
+        $collection->addCollection($collection1);
+        $collection->add('last', $last = new Route('/last'));
+
+        $this->assertInstanceOf('\ArrayIterator', $collection->getIterator());
+        $this->assertSame(array('bar' => $bar, 'foo' => $foo, 'last' => $last), $collection->getIterator()->getArrayCopy());
+    }
+
+    public function testCount()
+    {
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/foo'));
+
+        $collection1 = new RouteCollection();
+        $collection1->add('bar', new Route('/bar'));
+        $collection->addCollection($collection1);
+
+        $this->assertCount(2, $collection);
+    }
+
+    public function testAddCollection()
+    {
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/foo'));
+
+        $collection1 = new RouteCollection();
+        $collection1->add('bar', $bar = new Route('/bar'));
+        $collection1->add('foo', $foo = new Route('/foo-new'));
+
+        $collection2 = new RouteCollection();
+        $collection2->add('grandchild', $grandchild = new Route('/grandchild'));
+
+        $collection1->addCollection($collection2);
+        $collection->addCollection($collection1);
+        $collection->add('last', $last = new Route('/last'));
+
+        $this->assertSame(array('bar' => $bar, 'foo' => $foo, 'grandchild' => $grandchild, 'last' => $last), $collection->all(),
+            '->addCollection() imports routes of another collection, overrides if necessary and adds them at the end');
+    }
+
+    public function testAddCollectionWithResources()
+    {
+        if (!class_exists('Symfony\Component\Config\Resource\FileResource')) {
+            $this->markTestSkipped('The "Config" component is not available');
+        }
+
+        $collection = new RouteCollection();
+        $collection->addResource($foo = new FileResource(__DIR__.'/Fixtures/foo.xml'));
+        $collection1 = new RouteCollection();
+        $collection1->addResource($foo1 = new FileResource(__DIR__.'/Fixtures/foo1.xml'));
+        $collection->addCollection($collection1);
+        $this->assertEquals(array($foo, $foo1), $collection->getResources(), '->addCollection() merges resources');
+    }
+
+    public function testAddDefaultsAndRequirementsAndOptions()
+    {
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/{placeholder}'));
+        $collection1 = new RouteCollection();
+        $collection1->add('bar', new Route('/{placeholder}',
+            array('_controller' => 'fixed', 'placeholder' => 'default'), array('placeholder' => '.+'), array('option' => 'value'))
+        );
+        $collection->addCollection($collection1);
+
+        $collection->addDefaults(array('placeholder' => 'new-default'));
+        $this->assertEquals(array('placeholder' => 'new-default'), $collection->get('foo')->getDefaults(), '->addDefaults() adds defaults to all routes');
+        $this->assertEquals(array('_controller' => 'fixed', 'placeholder' => 'new-default'), $collection->get('bar')->getDefaults(),
+            '->addDefaults() adds defaults to all routes and overwrites existing ones');
+
+        $collection->addRequirements(array('placeholder' => '\d+'));
+        $this->assertEquals(array('placeholder' => '\d+'), $collection->get('foo')->getRequirements(), '->addRequirements() adds requirements to all routes');
+        $this->assertEquals(array('placeholder' => '\d+'), $collection->get('bar')->getRequirements(),
+            '->addRequirements() adds requirements to all routes and overwrites existing ones');
+
+        $collection->addOptions(array('option' => 'new-value'));
+        $this->assertEquals(
+            array('option' => 'new-value', 'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler'),
+            $collection->get('bar')->getOptions(), '->addOptions() adds options to all routes and overwrites existing ones'
+        );
+    }
+
+    public function testAddPrefix()
+    {
+        $collection = new RouteCollection();
+        $collection->add('foo', $foo = new Route('/foo'));
+        $collection2 = new RouteCollection();
+        $collection2->add('bar', $bar = new Route('/bar'));
+        $collection->addCollection($collection2);
+        $collection->addPrefix(' / ');
+        $this->assertSame('/foo', $collection->get('foo')->getPattern(), '->addPrefix() trims the prefix and a single slash has no effect');
+        $collection->addPrefix('/{admin}', array('admin' => 'admin'), array('admin' => '\d+'));
+        $this->assertEquals('/{admin}/foo', $collection->get('foo')->getPath(), '->addPrefix() adds a prefix to all routes');
+        $this->assertEquals('/{admin}/bar', $collection->get('bar')->getPath(), '->addPrefix() adds a prefix to all routes');
+        $this->assertEquals(array('admin' => 'admin'), $collection->get('foo')->getDefaults(), '->addPrefix() adds defaults to all routes');
+        $this->assertEquals(array('admin' => 'admin'), $collection->get('bar')->getDefaults(), '->addPrefix() adds defaults to all routes');
+        $this->assertEquals(array('admin' => '\d+'), $collection->get('foo')->getRequirements(), '->addPrefix() adds requirements to all routes');
+        $this->assertEquals(array('admin' => '\d+'), $collection->get('bar')->getRequirements(), '->addPrefix() adds requirements to all routes');
+        $collection->addPrefix('0');
+        $this->assertEquals('/0/{admin}/foo', $collection->get('foo')->getPattern(), '->addPrefix() ensures a prefix must start with a slash and must not end with a slash');
+        $collection->addPrefix('/ /');
+        $this->assertSame('/ /0/{admin}/foo', $collection->get('foo')->getPath(), '->addPrefix() can handle spaces if desired');
+        $this->assertSame('/ /0/{admin}/bar', $collection->get('bar')->getPath(), 'the route pattern of an added collection is in synch with the added prefix');
+    }
+
+    public function testAddPrefixOverridesDefaultsAndRequirements()
+    {
+        $collection = new RouteCollection();
+        $collection->add('foo', $foo = new Route('/foo'));
+        $collection->add('bar', $bar = new Route('/bar', array(), array('_scheme' => 'http')));
+        $collection->addPrefix('/admin', array(), array('_scheme' => 'https'));
+
+        $this->assertEquals('https', $collection->get('foo')->getRequirement('_scheme'), '->addPrefix() overrides existing requirements');
+        $this->assertEquals('https', $collection->get('bar')->getRequirement('_scheme'), '->addPrefix() overrides existing requirements');
+    }
+
+    public function testResource()
+    {
+        if (!class_exists('Symfony\Component\Config\Resource\FileResource')) {
+            $this->markTestSkipped('The "Config" component is not available');
+        }
+
+        $collection = new RouteCollection();
+        $collection->addResource($foo = new FileResource(__DIR__.'/Fixtures/foo.xml'));
+        $collection->addResource($bar = new FileResource(__DIR__.'/Fixtures/bar.xml'));
+        $collection->addResource(new FileResource(__DIR__.'/Fixtures/foo.xml'));
+
+        $this->assertEquals(array($foo, $bar), $collection->getResources(),
+            '->addResource() adds a resource and getResources() only returns unique ones by comparing the string representation');
+    }
+
+    public function testUniqueRouteWithGivenName()
+    {
+        $collection1 = new RouteCollection();
+        $collection1->add('foo', new Route('/old'));
+        $collection2 = new RouteCollection();
+        $collection3 = new RouteCollection();
+        $collection3->add('foo', $new = new Route('/new'));
+
+        $collection2->addCollection($collection3);
+        $collection1->addCollection($collection2);
+
+        $this->assertSame($new, $collection1->get('foo'), '->get() returns new route that overrode previous one');
+        // size of 1 because collection1 contains /new but not /old anymore
+        $this->assertCount(1, $collection1->getIterator(), '->addCollection() removes previous routes when adding new routes with the same name');
+    }
+
+    public function testGet()
+    {
+        $collection1 = new RouteCollection();
+        $collection1->add('a', $a = new Route('/a'));
+        $collection2 = new RouteCollection();
+        $collection2->add('b', $b = new Route('/b'));
+        $collection1->addCollection($collection2);
+        $collection1->add('$péß^a|', $c = new Route('/special'));
+
+        $this->assertSame($b, $collection1->get('b'), '->get() returns correct route in child collection');
+        $this->assertSame($c, $collection1->get('$péß^a|'), '->get() can handle special characters');
+        $this->assertNull($collection2->get('a'), '->get() does not return the route defined in parent collection');
+        $this->assertNull($collection1->get('non-existent'), '->get() returns null when route does not exist');
+        $this->assertNull($collection1->get(0), '->get() does not disclose internal child RouteCollection');
+    }
+
+    public function testRemove()
+    {
+        $collection = new RouteCollection();
+        $collection->add('foo', $foo = new Route('/foo'));
+
+        $collection1 = new RouteCollection();
+        $collection1->add('bar', $bar = new Route('/bar'));
+        $collection->addCollection($collection1);
+        $collection->add('last', $last = new Route('/last'));
+
+        $collection->remove('foo');
+        $this->assertSame(array('bar' => $bar, 'last' => $last), $collection->all(), '->remove() can remove a single route');
+        $collection->remove(array('bar', 'last'));
+        $this->assertSame(array(), $collection->all(), '->remove() accepts an array and can remove multiple routes at once');
+    }
+
+    public function testSetHost()
+    {
+        $collection = new RouteCollection();
+        $routea = new Route('/a');
+        $routeb = new Route('/b', array(), array(), array(), '{locale}.example.net');
+        $collection->add('a', $routea);
+        $collection->add('b', $routeb);
+
+        $collection->setHost('{locale}.example.com');
+
+        $this->assertEquals('{locale}.example.com', $routea->getHost());
+        $this->assertEquals('{locale}.example.com', $routeb->getHost());
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/RouteCompilerTest.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/RouteCompilerTest.php
new file mode 100644 (file)
index 0000000..d663ae9
--- /dev/null
@@ -0,0 +1,253 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests;
+
+use Symfony\Component\Routing\Route;
+
+class RouteCompilerTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @dataProvider provideCompileData
+     */
+    public function testCompile($name, $arguments, $prefix, $regex, $variables, $tokens)
+    {
+        $r = new \ReflectionClass('Symfony\\Component\\Routing\\Route');
+        $route = $r->newInstanceArgs($arguments);
+
+        $compiled = $route->compile();
+        $this->assertEquals($prefix, $compiled->getStaticPrefix(), $name.' (static prefix)');
+        $this->assertEquals($regex, $compiled->getRegex(), $name.' (regex)');
+        $this->assertEquals($variables, $compiled->getVariables(), $name.' (variables)');
+        $this->assertEquals($tokens, $compiled->getTokens(), $name.' (tokens)');
+    }
+
+    public function provideCompileData()
+    {
+        return array(
+            array(
+                'Static route',
+                array('/foo'),
+                '/foo', '#^/foo$#s', array(), array(
+                    array('text', '/foo'),
+                )),
+
+            array(
+                'Route with a variable',
+                array('/foo/{bar}'),
+                '/foo', '#^/foo/(?P<bar>[^/]++)$#s', array('bar'), array(
+                    array('variable', '/', '[^/]++', 'bar'),
+                    array('text', '/foo'),
+                )),
+
+            array(
+                'Route with a variable that has a default value',
+                array('/foo/{bar}', array('bar' => 'bar')),
+                '/foo', '#^/foo(?:/(?P<bar>[^/]++))?$#s', array('bar'), array(
+                    array('variable', '/', '[^/]++', 'bar'),
+                    array('text', '/foo'),
+                )),
+
+            array(
+                'Route with several variables',
+                array('/foo/{bar}/{foobar}'),
+                '/foo', '#^/foo/(?P<bar>[^/]++)/(?P<foobar>[^/]++)$#s', array('bar', 'foobar'), array(
+                    array('variable', '/', '[^/]++', 'foobar'),
+                    array('variable', '/', '[^/]++', 'bar'),
+                    array('text', '/foo'),
+                )),
+
+            array(
+                'Route with several variables that have default values',
+                array('/foo/{bar}/{foobar}', array('bar' => 'bar', 'foobar' => '')),
+                '/foo', '#^/foo(?:/(?P<bar>[^/]++)(?:/(?P<foobar>[^/]++))?)?$#s', array('bar', 'foobar'), array(
+                    array('variable', '/', '[^/]++', 'foobar'),
+                    array('variable', '/', '[^/]++', 'bar'),
+                    array('text', '/foo'),
+                )),
+
+            array(
+                'Route with several variables but some of them have no default values',
+                array('/foo/{bar}/{foobar}', array('bar' => 'bar')),
+                '/foo', '#^/foo/(?P<bar>[^/]++)/(?P<foobar>[^/]++)$#s', array('bar', 'foobar'), array(
+                    array('variable', '/', '[^/]++', 'foobar'),
+                    array('variable', '/', '[^/]++', 'bar'),
+                    array('text', '/foo'),
+                )),
+
+            array(
+                'Route with an optional variable as the first segment',
+                array('/{bar}', array('bar' => 'bar')),
+                '', '#^/(?P<bar>[^/]++)?$#s', array('bar'), array(
+                    array('variable', '/', '[^/]++', 'bar'),
+                )),
+
+            array(
+                'Route with a requirement of 0',
+                array('/{bar}', array('bar' => null), array('bar' => '0')),
+                '', '#^/(?P<bar>0)?$#s', array('bar'), array(
+                    array('variable', '/', '0', 'bar'),
+                )),
+
+            array(
+                'Route with an optional variable as the first segment with requirements',
+                array('/{bar}', array('bar' => 'bar'), array('bar' => '(foo|bar)')),
+                '', '#^/(?P<bar>(foo|bar))?$#s', array('bar'), array(
+                    array('variable', '/', '(foo|bar)', 'bar'),
+                )),
+
+            array(
+                'Route with only optional variables',
+                array('/{foo}/{bar}', array('foo' => 'foo', 'bar' => 'bar')),
+                '', '#^/(?P<foo>[^/]++)?(?:/(?P<bar>[^/]++))?$#s', array('foo', 'bar'), array(
+                    array('variable', '/', '[^/]++', 'bar'),
+                    array('variable', '/', '[^/]++', 'foo'),
+                )),
+
+            array(
+                'Route with a variable in last position',
+                array('/foo-{bar}'),
+                '/foo', '#^/foo\-(?P<bar>[^/]++)$#s', array('bar'), array(
+                array('variable', '-', '[^/]++', 'bar'),
+                array('text', '/foo'),
+            )),
+
+            array(
+                'Route with nested placeholders',
+                array('/{static{var}static}'),
+                '/{static', '#^/\{static(?P<var>[^/]+)static\}$#s', array('var'), array(
+                array('text', 'static}'),
+                array('variable', '', '[^/]+', 'var'),
+                array('text', '/{static'),
+            )),
+
+            array(
+                'Route without separator between variables',
+                array('/{w}{x}{y}{z}.{_format}', array('z' => 'default-z', '_format' => 'html'), array('y' => '(y|Y)')),
+                '', '#^/(?P<w>[^/\.]+)(?P<x>[^/\.]+)(?P<y>(y|Y))(?:(?P<z>[^/\.]++)(?:\.(?P<_format>[^/]++))?)?$#s', array('w', 'x', 'y', 'z', '_format'), array(
+                array('variable', '.', '[^/]++', '_format'),
+                array('variable', '', '[^/\.]++', 'z'),
+                array('variable', '', '(y|Y)', 'y'),
+                array('variable', '', '[^/\.]+', 'x'),
+                array('variable', '/', '[^/\.]+', 'w'),
+            )),
+
+            array(
+                'Route with a format',
+                array('/foo/{bar}.{_format}'),
+                '/foo', '#^/foo/(?P<bar>[^/\.]++)\.(?P<_format>[^/]++)$#s', array('bar', '_format'), array(
+                array('variable', '.', '[^/]++', '_format'),
+                array('variable', '/', '[^/\.]++', 'bar'),
+                array('text', '/foo'),
+            )),
+        );
+    }
+
+    /**
+     * @expectedException \LogicException
+     */
+    public function testRouteWithSameVariableTwice()
+    {
+        $route = new Route('/{name}/{name}');
+
+        $compiled = $route->compile();
+    }
+
+    /**
+     * @dataProvider getNumericVariableNames
+     * @expectedException \DomainException
+     */
+    public function testRouteWithNumericVariableName($name)
+    {
+        $route = new Route('/{'. $name.'}');
+        $route->compile();
+    }
+
+    public function getNumericVariableNames()
+    {
+        return array(
+           array('09'),
+           array('123'),
+           array('1e2')
+        );
+    }
+
+    /**
+     * @dataProvider provideCompileWithHostData
+     */
+    public function testCompileWithHost($name, $arguments, $prefix, $regex, $variables, $pathVariables, $tokens, $hostRegex, $hostVariables, $hostTokens)
+    {
+        $r = new \ReflectionClass('Symfony\\Component\\Routing\\Route');
+        $route = $r->newInstanceArgs($arguments);
+
+        $compiled = $route->compile();
+        $this->assertEquals($prefix, $compiled->getStaticPrefix(), $name.' (static prefix)');
+        $this->assertEquals($regex, str_replace(array("\n", ' '), '', $compiled->getRegex()), $name.' (regex)');
+        $this->assertEquals($variables, $compiled->getVariables(), $name.' (variables)');
+        $this->assertEquals($pathVariables, $compiled->getPathVariables(), $name.' (path variables)');
+        $this->assertEquals($tokens, $compiled->getTokens(), $name.' (tokens)');
+        $this->assertEquals($hostRegex, str_replace(array("\n", ' '), '', $compiled->getHostRegex()), $name.' (host regex)');
+        $this->assertEquals($hostVariables, $compiled->getHostVariables(), $name.' (host variables)');
+        $this->assertEquals($hostTokens, $compiled->getHostTokens(), $name.' (host tokens)');
+    }
+
+    public function provideCompileWithHostData()
+    {
+        return array(
+            array(
+                'Route with host pattern',
+                array('/hello', array(), array(), array(), 'www.example.com'),
+                '/hello', '#^/hello$#s', array(), array(), array(
+                    array('text', '/hello'),
+                ),
+                '#^www\.example\.com$#s', array(), array(
+                    array('text', 'www.example.com'),
+                ),
+            ),
+            array(
+                'Route with host pattern and some variables',
+                array('/hello/{name}', array(), array(), array(), 'www.example.{tld}'),
+                '/hello', '#^/hello/(?P<name>[^/]++)$#s', array('tld', 'name'), array('name'), array(
+                    array('variable', '/', '[^/]++', 'name'),
+                    array('text', '/hello'),
+                ),
+                '#^www\.example\.(?P<tld>[^\.]++)$#s', array('tld'), array(
+                    array('variable', '.', '[^\.]++', 'tld'),
+                    array('text', 'www.example'),
+                ),
+            ),
+            array(
+                'Route with variable at beginning of host',
+                array('/hello', array(), array(), array(), '{locale}.example.{tld}'),
+                '/hello', '#^/hello$#s', array('locale', 'tld'), array(), array(
+                    array('text', '/hello'),
+                ),
+                '#^(?P<locale>[^\.]++)\.example\.(?P<tld>[^\.]++)$#s', array('locale', 'tld'), array(
+                    array('variable', '.', '[^\.]++', 'tld'),
+                    array('text', '.example'),
+                    array('variable', '', '[^\.]++', 'locale'),
+                ),
+            ),
+            array(
+                'Route with host variables that has a default value',
+                array('/hello', array('locale' => 'a', 'tld' => 'b'), array(), array(), '{locale}.example.{tld}'),
+                '/hello', '#^/hello$#s', array('locale', 'tld'), array(), array(
+                    array('text', '/hello'),
+                ),
+                '#^(?P<locale>[^\.]++)\.example\.(?P<tld>[^\.]++)$#s', array('locale', 'tld'), array(
+                    array('variable', '.', '[^\.]++', 'tld'),
+                    array('text', '.example'),
+                    array('variable', '', '[^\.]++', 'locale'),
+                ),
+            ),
+        );
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/RouteTest.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/RouteTest.php
new file mode 100644 (file)
index 0000000..31f1066
--- /dev/null
@@ -0,0 +1,192 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests;
+
+use Symfony\Component\Routing\Route;
+
+class RouteTest extends \PHPUnit_Framework_TestCase
+{
+    public function testConstructor()
+    {
+        $route = new Route('/{foo}', array('foo' => 'bar'), array('foo' => '\d+'), array('foo' => 'bar'), '{locale}.example.com');
+        $this->assertEquals('/{foo}', $route->getPath(), '__construct() takes a path as its first argument');
+        $this->assertEquals(array('foo' => 'bar'), $route->getDefaults(), '__construct() takes defaults as its second argument');
+        $this->assertEquals(array('foo' => '\d+'), $route->getRequirements(), '__construct() takes requirements as its third argument');
+        $this->assertEquals('bar', $route->getOption('foo'), '__construct() takes options as its fourth argument');
+        $this->assertEquals('{locale}.example.com', $route->getHost(), '__construct() takes a host pattern as its fifth argument');
+
+        $route = new Route('/', array(), array(), array(), '', array('Https'), array('POST', 'put'));
+        $this->assertEquals(array('https'), $route->getSchemes(), '__construct() takes schemes as its sixth argument and lowercases it');
+        $this->assertEquals(array('POST', 'PUT'), $route->getMethods(), '__construct() takes methods as its seventh argument and uppercases it');
+
+        $route = new Route('/', array(), array(), array(), '', 'Https', 'Post');
+        $this->assertEquals(array('https'), $route->getSchemes(), '__construct() takes a single scheme as its sixth argument');
+        $this->assertEquals(array('POST'), $route->getMethods(), '__construct() takes a single method as its seventh argument');
+    }
+
+    public function testPath()
+    {
+        $route = new Route('/{foo}');
+        $route->setPath('/{bar}');
+        $this->assertEquals('/{bar}', $route->getPath(), '->setPath() sets the path');
+        $route->setPath('');
+        $this->assertEquals('/', $route->getPath(), '->setPath() adds a / at the beginning of the path if needed');
+        $route->setPath('bar');
+        $this->assertEquals('/bar', $route->getPath(), '->setPath() adds a / at the beginning of the path if needed');
+        $this->assertEquals($route, $route->setPath(''), '->setPath() implements a fluent interface');
+        $route->setPath('//path');
+        $this->assertEquals('/path', $route->getPath(), '->setPath() does not allow two slahes "//" at the beginning of the path as it would be confused with a network path when generating the path from the route');
+    }
+
+    public function testOptions()
+    {
+        $route = new Route('/{foo}');
+        $route->setOptions(array('foo' => 'bar'));
+        $this->assertEquals(array_merge(array(
+        'compiler_class'     => 'Symfony\\Component\\Routing\\RouteCompiler',
+        ), array('foo' => 'bar')), $route->getOptions(), '->setOptions() sets the options');
+        $this->assertEquals($route, $route->setOptions(array()), '->setOptions() implements a fluent interface');
+
+        $route->setOptions(array('foo' => 'foo'));
+        $route->addOptions(array('bar' => 'bar'));
+        $this->assertEquals($route, $route->addOptions(array()), '->addOptions() implements a fluent interface');
+        $this->assertEquals(array('foo' => 'foo', 'bar' => 'bar', 'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler'), $route->getOptions(), '->addDefaults() keep previous defaults');
+    }
+
+    public function testDefaults()
+    {
+        $route = new Route('/{foo}');
+        $route->setDefaults(array('foo' => 'bar'));
+        $this->assertEquals(array('foo' => 'bar'), $route->getDefaults(), '->setDefaults() sets the defaults');
+        $this->assertEquals($route, $route->setDefaults(array()), '->setDefaults() implements a fluent interface');
+
+        $route->setDefault('foo', 'bar');
+        $this->assertEquals('bar', $route->getDefault('foo'), '->setDefault() sets a default value');
+
+        $route->setDefault('foo2', 'bar2');
+        $this->assertEquals('bar2', $route->getDefault('foo2'), '->getDefault() return the default value');
+        $this->assertNull($route->getDefault('not_defined'), '->getDefault() return null if default value is not setted');
+
+        $route->setDefault('_controller', $closure = function () { return 'Hello'; });
+        $this->assertEquals($closure, $route->getDefault('_controller'), '->setDefault() sets a default value');
+
+        $route->setDefaults(array('foo' => 'foo'));
+        $route->addDefaults(array('bar' => 'bar'));
+        $this->assertEquals($route, $route->addDefaults(array()), '->addDefaults() implements a fluent interface');
+        $this->assertEquals(array('foo' => 'foo', 'bar' => 'bar'), $route->getDefaults(), '->addDefaults() keep previous defaults');
+    }
+
+    public function testRequirements()
+    {
+        $route = new Route('/{foo}');
+        $route->setRequirements(array('foo' => '\d+'));
+        $this->assertEquals(array('foo' => '\d+'), $route->getRequirements(), '->setRequirements() sets the requirements');
+        $this->assertEquals('\d+', $route->getRequirement('foo'), '->getRequirement() returns a requirement');
+        $this->assertNull($route->getRequirement('bar'), '->getRequirement() returns null if a requirement is not defined');
+        $route->setRequirements(array('foo' => '^\d+$'));
+        $this->assertEquals('\d+', $route->getRequirement('foo'), '->getRequirement() removes ^ and $ from the path');
+        $this->assertEquals($route, $route->setRequirements(array()), '->setRequirements() implements a fluent interface');
+
+        $route->setRequirements(array('foo' => '\d+'));
+        $route->addRequirements(array('bar' => '\d+'));
+        $this->assertEquals($route, $route->addRequirements(array()), '->addRequirements() implements a fluent interface');
+        $this->assertEquals(array('foo' => '\d+', 'bar' => '\d+'), $route->getRequirements(), '->addRequirement() keep previous requirements');
+    }
+
+    public function testRequirement()
+    {
+        $route = new Route('/{foo}');
+        $route->setRequirement('foo', '^\d+$');
+        $this->assertEquals('\d+', $route->getRequirement('foo'), '->setRequirement() removes ^ and $ from the path');
+    }
+
+    /**
+     * @dataProvider getInvalidRequirements
+     * @expectedException \InvalidArgumentException
+     */
+    public function testSetInvalidRequirement($req)
+    {
+        $route = new Route('/{foo}');
+        $route->setRequirement('foo', $req);
+    }
+
+    public function getInvalidRequirements()
+    {
+        return array(
+           array(''),
+           array(array()),
+           array('^$'),
+           array('^'),
+           array('$')
+        );
+    }
+
+    public function testHost()
+    {
+        $route = new Route('/');
+        $route->setHost('{locale}.example.net');
+        $this->assertEquals('{locale}.example.net', $route->getHost(), '->setHost() sets the host pattern');
+    }
+
+    public function testScheme()
+    {
+        $route = new Route('/');
+        $this->assertEquals(array(), $route->getSchemes(), 'schemes is initialized with array()');
+        $route->setSchemes('hTTp');
+        $this->assertEquals(array('http'), $route->getSchemes(), '->setSchemes() accepts a single scheme string and lowercases it');
+        $route->setSchemes(array('HttpS', 'hTTp'));
+        $this->assertEquals(array('https', 'http'), $route->getSchemes(), '->setSchemes() accepts an array of schemes and lowercases them');
+    }
+
+    public function testSchemeIsBC()
+    {
+        $route = new Route('/');
+        $route->setRequirement('_scheme', 'http|https');
+        $this->assertEquals('http|https', $route->getRequirement('_scheme'));
+        $this->assertEquals(array('http', 'https'), $route->getSchemes());
+        $route->setSchemes(array('hTTp'));
+        $this->assertEquals('http', $route->getRequirement('_scheme'));
+        $route->setSchemes(array());
+        $this->assertNull($route->getRequirement('_scheme'));
+    }
+
+    public function testMethod()
+    {
+        $route = new Route('/');
+        $this->assertEquals(array(), $route->getMethods(), 'methods is initialized with array()');
+        $route->setMethods('gEt');
+        $this->assertEquals(array('GET'), $route->getMethods(), '->setMethods() accepts a single method string and uppercases it');
+        $route->setMethods(array('gEt', 'PosT'));
+        $this->assertEquals(array('GET', 'POST'), $route->getMethods(), '->setMethods() accepts an array of methods and uppercases them');
+    }
+
+    public function testMethodIsBC()
+    {
+        $route = new Route('/');
+        $route->setRequirement('_method', 'GET|POST');
+        $this->assertEquals('GET|POST', $route->getRequirement('_method'));
+        $this->assertEquals(array('GET', 'POST'), $route->getMethods());
+        $route->setMethods(array('gEt'));
+        $this->assertEquals('GET', $route->getRequirement('_method'));
+        $route->setMethods(array());
+        $this->assertNull($route->getRequirement('_method'));
+    }
+
+    public function testCompile()
+    {
+        $route = new Route('/{foo}');
+        $this->assertInstanceOf('Symfony\Component\Routing\CompiledRoute', $compiled = $route->compile(), '->compile() returns a compiled route');
+        $this->assertSame($compiled, $route->compile(), '->compile() only compiled the route once if unchanged');
+        $route->setRequirement('foo', '.*');
+        $this->assertNotSame($compiled, $route->compile(), '->compile() recompiles if the route was modified');
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/Tests/RouterTest.php b/vendor/symfony/routing/Symfony/Component/Routing/Tests/RouterTest.php
new file mode 100644 (file)
index 0000000..a3c336e
--- /dev/null
@@ -0,0 +1,138 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests;
+
+use Symfony\Component\Routing\Router;
+
+class RouterTest extends \PHPUnit_Framework_TestCase
+{
+    private $router = null;
+
+    private $loader = null;
+
+    protected function setUp()
+    {
+        $this->loader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface');
+        $this->router = new Router($this->loader, 'routing.yml');
+    }
+
+    public function testSetOptionsWithSupportedOptions()
+    {
+        $this->router->setOptions(array(
+            'cache_dir' => './cache',
+            'debug' => true,
+            'resource_type' => 'ResourceType'
+        ));
+
+        $this->assertSame('./cache', $this->router->getOption('cache_dir'));
+        $this->assertTrue($this->router->getOption('debug'));
+        $this->assertSame('ResourceType', $this->router->getOption('resource_type'));
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     * @expectedExceptionMessage The Router does not support the following options: "option_foo", "option_bar"
+     */
+    public function testSetOptionsWithUnsupportedOptions()
+    {
+        $this->router->setOptions(array(
+            'cache_dir' => './cache',
+            'option_foo' => true,
+            'option_bar' => 'baz',
+            'resource_type' => 'ResourceType'
+        ));
+    }
+
+    public function testSetOptionWithSupportedOption()
+    {
+        $this->router->setOption('cache_dir', './cache');
+
+        $this->assertSame('./cache', $this->router->getOption('cache_dir'));
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     * @expectedExceptionMessage The Router does not support the "option_foo" option
+     */
+    public function testSetOptionWithUnsupportedOption()
+    {
+        $this->router->setOption('option_foo', true);
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     * @expectedExceptionMessage The Router does not support the "option_foo" option
+     */
+    public function testGetOptionWithUnsupportedOption()
+    {
+        $this->router->getOption('option_foo', true);
+    }
+
+    public function testThatRouteCollectionIsLoaded()
+    {
+        $this->router->setOption('resource_type', 'ResourceType');
+
+        $routeCollection = $this->getMock('Symfony\Component\Routing\RouteCollection');
+
+        $this->loader->expects($this->once())
+            ->method('load')->with('routing.yml', 'ResourceType')
+            ->will($this->returnValue($routeCollection));
+
+        $this->assertSame($routeCollection, $this->router->getRouteCollection());
+    }
+
+    /**
+     * @dataProvider provideMatcherOptionsPreventingCaching
+     */
+    public function testMatcherIsCreatedIfCacheIsNotConfigured($option)
+    {
+        $this->router->setOption($option, null);
+
+        $this->loader->expects($this->once())
+            ->method('load')->with('routing.yml', null)
+            ->will($this->returnValue($this->getMock('Symfony\Component\Routing\RouteCollection')));
+
+        $this->assertInstanceOf('Symfony\\Component\\Routing\\Matcher\\UrlMatcher', $this->router->getMatcher());
+
+    }
+
+    public function provideMatcherOptionsPreventingCaching()
+    {
+        return array(
+            array('cache_dir'),
+            array('matcher_cache_class')
+        );
+    }
+
+    /**
+     * @dataProvider provideGeneratorOptionsPreventingCaching
+     */
+    public function testGeneratorIsCreatedIfCacheIsNotConfigured($option)
+    {
+        $this->router->setOption($option, null);
+
+        $this->loader->expects($this->once())
+            ->method('load')->with('routing.yml', null)
+            ->will($this->returnValue($this->getMock('Symfony\Component\Routing\RouteCollection')));
+
+        $this->assertInstanceOf('Symfony\\Component\\Routing\\Generator\\UrlGenerator', $this->router->getGenerator());
+
+    }
+
+    public function provideGeneratorOptionsPreventingCaching()
+    {
+        return array(
+            array('cache_dir'),
+            array('generator_cache_class')
+        );
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/composer.json b/vendor/symfony/routing/Symfony/Component/Routing/composer.json
new file mode 100644 (file)
index 0000000..9a737c6
--- /dev/null
@@ -0,0 +1,42 @@
+{
+    "name": "symfony/routing",
+    "type": "library",
+    "description": "Symfony Routing 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/config": "~2.2",
+        "symfony/yaml": "~2.0",
+        "doctrine/common": "~2.2",
+        "psr/log": "~1.0"
+    },
+    "suggest": {
+        "symfony/config": "",
+        "symfony/yaml": "",
+        "doctrine/common": ""
+    },
+    "autoload": {
+        "psr-0": { "Symfony\\Component\\Routing\\": "" }
+    },
+    "target-dir": "Symfony/Component/Routing",
+    "minimum-stability": "dev",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "2.3-dev"
+        }
+    }
+}
diff --git a/vendor/symfony/routing/Symfony/Component/Routing/phpunit.xml.dist b/vendor/symfony/routing/Symfony/Component/Routing/phpunit.xml.dist
new file mode 100644 (file)
index 0000000..830066a
--- /dev/null
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         processIsolation="false"
+         stopOnFailure="false"
+         syntaxCheck="false"
+         bootstrap="vendor/autoload.php"
+>
+    <testsuites>
+        <testsuite name="Symfony Routing Component Test Suite">
+            <directory>./Tests/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./</directory>
+            <exclude>
+                <directory>./vendor</directory>
+                <directory>./Tests</directory>
+            </exclude>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/.gitignore b/vendor/symfony/translation/Symfony/Component/Translation/.gitignore
new file mode 100644 (file)
index 0000000..44de97a
--- /dev/null
@@ -0,0 +1,4 @@
+vendor/
+composer.lock
+phpunit.xml
+
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/CHANGELOG.md b/vendor/symfony/translation/Symfony/Component/Translation/CHANGELOG.md
new file mode 100644 (file)
index 0000000..b8027ab
--- /dev/null
@@ -0,0 +1,27 @@
+CHANGELOG
+=========
+
+2.3.0
+-----
+
+ * added classes to make operations on catalogues (like making a diff or a merge on 2 catalogues)
+ * added Translator::getFallbackLocales()
+ * deprecated Translator::setFallbackLocale() in favor of the new Translator::setFallbackLocales() method
+
+2.2.0
+-----
+
+ * QtTranslationsLoader class renamed to QtFileLoader. QtTranslationsLoader is deprecated and will be removed in 2.3.
+ * [BC BREAK] uniformized the exception thrown by the load() method when an error occurs. The load() method now
+   throws Symfony\Component\Translation\Exception\NotFoundResourceException when a resource cannot be found
+   and Symfony\Component\Translation\Exception\InvalidResourceException when a resource is invalid.
+ * changed the exception class thrown by some load() methods from \RuntimeException to \InvalidArgumentException
+   (IcuDatFileLoader, IcuResFileLoader and QtFileLoader)
+
+2.1.0
+-----
+
+ * added support for more than one fallback locale
+ * added support for extracting translation messages from templates (Twig and PHP)
+ * added dumpers for translation catalogs
+ * added support for QT, gettext, and ResourceBundles
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Catalogue/AbstractOperation.php b/vendor/symfony/translation/Symfony/Component/Translation/Catalogue/AbstractOperation.php
new file mode 100644 (file)
index 0000000..062056b
--- /dev/null
@@ -0,0 +1,146 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Catalogue;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\MessageCatalogueInterface;
+
+/**
+ * Base catalogues binary operation class.
+ *
+ * @author Jean-François Simon <contact@jfsimon.fr>
+ */
+abstract class AbstractOperation implements OperationInterface
+{
+    /**
+     * @var MessageCatalogueInterface
+     */
+    protected $source;
+
+    /**
+     * @var MessageCatalogueInterface
+     */
+    protected $target;
+
+    /**
+     * @var MessageCatalogue
+     */
+    protected $result;
+
+    /**
+     * @var null|array
+     */
+    private $domains;
+
+    /**
+     * @var array
+     */
+    protected $messages;
+
+    /**
+     * @param MessageCatalogueInterface $source
+     * @param MessageCatalogueInterface $target
+     *
+     * @throws \LogicException
+     */
+    public function __construct(MessageCatalogueInterface $source, MessageCatalogueInterface $target)
+    {
+        if ($source->getLocale() !== $target->getLocale()) {
+            throw new \LogicException('Operated catalogues must belong to the same locale.');
+        }
+
+        $this->source = $source;
+        $this->target = $target;
+        $this->result = new MessageCatalogue($source->getLocale());
+        $this->domains = null;
+        $this->messages = array();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDomains()
+    {
+        if (null === $this->domains) {
+            $this->domains = array_values(array_unique(array_merge($this->source->getDomains(), $this->target->getDomains())));
+        }
+
+        return $this->domains;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getMessages($domain)
+    {
+        if (!in_array($domain, $this->getDomains())) {
+            throw new \InvalidArgumentException(sprintf('Invalid domain: %s.', $domain));
+        }
+
+        if (!isset($this->messages[$domain]['all'])) {
+            $this->processDomain($domain);
+        }
+
+        return $this->messages[$domain]['all'];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getNewMessages($domain)
+    {
+        if (!in_array($domain, $this->getDomains())) {
+            throw new \InvalidArgumentException(sprintf('Invalid domain: %s.', $domain));
+        }
+
+        if (!isset($this->messages[$domain]['new'])) {
+            $this->processDomain($domain);
+        }
+
+        return $this->messages[$domain]['new'];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getObsoleteMessages($domain)
+    {
+        if (!in_array($domain, $this->getDomains())) {
+            throw new \InvalidArgumentException(sprintf('Invalid domain: %s.', $domain));
+        }
+
+        if (!isset($this->messages[$domain]['obsolete'])) {
+            $this->processDomain($domain);
+        }
+
+        return $this->messages[$domain]['obsolete'];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getResult()
+    {
+        foreach ($this->getDomains() as $domain) {
+            if (!isset($this->messages[$domain])) {
+                $this->processDomain($domain);
+            }
+        }
+
+        return $this->result;
+    }
+
+    /**
+     * @param string $domain
+     */
+    abstract protected function processDomain($domain);
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Catalogue/DiffOperation.php b/vendor/symfony/translation/Symfony/Component/Translation/Catalogue/DiffOperation.php
new file mode 100644 (file)
index 0000000..1672d12
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Catalogue;
+
+/**
+ * Diff operation between two catalogues.
+ *
+ * @author Jean-François Simon <contact@jfsimon.fr>
+ */
+class DiffOperation extends AbstractOperation
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function processDomain($domain)
+    {
+        $this->messages[$domain] = array(
+            'all'      => array(),
+            'new'      => array(),
+            'obsolete' => array(),
+        );
+
+        foreach ($this->source->all($domain) as $id => $message) {
+            if ($this->target->has($id, $domain)) {
+                $this->messages[$domain]['all'][$id] = $message;
+                $this->result->add(array($id => $message), $domain);
+            } else {
+                $this->messages[$domain]['obsolete'][$id] = $message;
+            }
+        }
+
+        foreach ($this->target->all($domain) as $id => $message) {
+            if (!$this->source->has($id, $domain)) {
+                $this->messages[$domain]['all'][$id] = $message;
+                $this->messages[$domain]['new'][$id] = $message;
+                $this->result->add(array($id => $message), $domain);
+            }
+        }
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Catalogue/MergeOperation.php b/vendor/symfony/translation/Symfony/Component/Translation/Catalogue/MergeOperation.php
new file mode 100644 (file)
index 0000000..0052363
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Catalogue;
+
+/**
+ * Merge operation between two catalogues.
+ *
+ * @author Jean-François Simon <contact@jfsimon.fr>
+ */
+class MergeOperation extends AbstractOperation
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function processDomain($domain)
+    {
+        $this->messages[$domain] = array(
+            'all'      => array(),
+            'new'      => array(),
+            'obsolete' => array(),
+        );
+
+        foreach ($this->source->all($domain) as $id => $message) {
+            $this->messages[$domain]['all'][$id] = $message;
+            $this->result->add(array($id => $message), $domain);
+        }
+
+        foreach ($this->target->all($domain) as $id => $message) {
+            if (!$this->source->has($id, $domain)) {
+                $this->messages[$domain]['all'][$id] = $message;
+                $this->messages[$domain]['new'][$id] = $message;
+                $this->result->add(array($id => $message), $domain);
+            }
+        }
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Catalogue/OperationInterface.php b/vendor/symfony/translation/Symfony/Component/Translation/Catalogue/OperationInterface.php
new file mode 100644 (file)
index 0000000..d72378a
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Catalogue;
+
+use Symfony\Component\Translation\MessageCatalogueInterface;
+
+/**
+ * Represents an operation on catalogue(s).
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ */
+interface OperationInterface
+{
+    /**
+     * Returns domains affected by operation.
+     *
+     * @return array
+     */
+    public function getDomains();
+
+    /**
+     * Returns all valid messages after operation.
+     *
+     * @param string $domain
+     *
+     * @return array
+     */
+    public function getMessages($domain);
+
+    /**
+     * Returns new messages after operation.
+     *
+     * @param string $domain
+     *
+     * @return array
+     */
+    public function getNewMessages($domain);
+
+    /**
+     * Returns obsolete messages after operation.
+     *
+     * @param string $domain
+     *
+     * @return array
+     */
+    public function getObsoleteMessages($domain);
+
+    /**
+     * Returns resulting catalogue.
+     *
+     * @return MessageCatalogueInterface
+     */
+    public function getResult();
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Dumper/CsvFileDumper.php b/vendor/symfony/translation/Symfony/Component/Translation/Dumper/CsvFileDumper.php
new file mode 100644 (file)
index 0000000..0b41190
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * CsvFileDumper generates a csv formatted string representation of a message catalogue.
+ *
+ * @author Stealth35
+ */
+class CsvFileDumper extends FileDumper
+{
+    private $delimiter = ';';
+    private $enclosure = '"';
+
+    /**
+     * {@inheritDoc}
+     */
+    public function format(MessageCatalogue $messages, $domain = 'messages')
+    {
+        $handle = fopen('php://memory', 'rb+');
+
+        foreach ($messages->all($domain) as $source => $target) {
+            fputcsv($handle, array($source, $target), $this->delimiter, $this->enclosure);
+        }
+
+        rewind($handle);
+        $output = stream_get_contents($handle);
+        fclose($handle);
+
+        return $output;
+    }
+
+    /**
+     * Sets the delimiter and escape character for CSV.
+     *
+     * @param string $delimiter delimiter character
+     * @param string $enclosure enclosure character
+     */
+    public function setCsvControl($delimiter = ';', $enclosure = '"')
+    {
+        $this->delimiter = $delimiter;
+        $this->enclosure = $enclosure;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected function getExtension()
+    {
+        return 'csv';
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Dumper/DumperInterface.php b/vendor/symfony/translation/Symfony/Component/Translation/Dumper/DumperInterface.php
new file mode 100644 (file)
index 0000000..cebc65e
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * DumperInterface is the interface implemented by all translation dumpers.
+ * There is no common option.
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+interface DumperInterface
+{
+    /**
+     * Dumps the message catalogue.
+     *
+     * @param MessageCatalogue $messages The message catalogue
+     * @param array            $options  Options that are used by the dumper
+     */
+    public function dump(MessageCatalogue $messages, $options = array());
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Dumper/FileDumper.php b/vendor/symfony/translation/Symfony/Component/Translation/Dumper/FileDumper.php
new file mode 100644 (file)
index 0000000..63c1e6c
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * FileDumper is an implementation of DumperInterface that dump a message catalogue to file(s).
+ * Performs backup of already existing files.
+ *
+ * Options:
+ * - path (mandatory): the directory where the files should be saved
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+abstract class FileDumper implements DumperInterface
+{
+    /**
+     * {@inheritDoc}
+     */
+    public function dump(MessageCatalogue $messages, $options = array())
+    {
+        if (!array_key_exists('path', $options)) {
+            throw new \InvalidArgumentException('The file dumper need a path options.');
+        }
+
+        // save a file for each domain
+        foreach ($messages->getDomains() as $domain) {
+            $file = $domain.'.'.$messages->getLocale().'.'.$this->getExtension();
+            // backup
+            $fullpath = $options['path'].'/'.$file;
+            if (file_exists($fullpath)) {
+                copy($fullpath, $fullpath.'~');
+            }
+            // save file
+            file_put_contents($fullpath, $this->format($messages, $domain));
+        }
+    }
+
+    /**
+     * Transforms a domain of a message catalogue to its string representation.
+     *
+     * @param MessageCatalogue $messages
+     * @param string           $domain
+     *
+     * @return string representation
+     */
+    abstract protected function format(MessageCatalogue $messages, $domain);
+
+    /**
+     * Gets the file extension of the dumper.
+     *
+     * @return string file extension
+     */
+    abstract protected function getExtension();
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Dumper/IcuResFileDumper.php b/vendor/symfony/translation/Symfony/Component/Translation/Dumper/IcuResFileDumper.php
new file mode 100644 (file)
index 0000000..979153a
--- /dev/null
@@ -0,0 +1,135 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * IcuResDumper generates an ICU ResourceBundle formatted string representation of a message catalogue.
+ *
+ * @author Stealth35
+ */
+class IcuResFileDumper implements DumperInterface
+{
+    /**
+     * {@inheritDoc}
+     */
+    public function dump(MessageCatalogue $messages, $options = array())
+    {
+        if (!array_key_exists('path', $options)) {
+            throw new \InvalidArgumentException('The file dumper need a path options.');
+        }
+
+        // save a file for each domain
+        foreach ($messages->getDomains() as $domain) {
+            $file = $messages->getLocale().'.'.$this->getExtension();
+            $path = $options['path'].'/'.$domain.'/';
+
+            if (!file_exists($path)) {
+                mkdir($path);
+            }
+
+            // backup
+            if (file_exists($path.$file)) {
+                copy($path.$file, $path.$file.'~');
+            }
+
+            // save file
+            file_put_contents($path.$file, $this->format($messages, $domain));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function format(MessageCatalogue $messages, $domain = 'messages')
+    {
+        $data = $indexes = $resources = '';
+
+        foreach ($messages->all($domain) as $source => $target) {
+            $indexes .= pack('v', strlen($data) + 28);
+            $data    .= $source."\0";
+        }
+
+        $data .= $this->writePadding($data);
+
+        $keyTop = $this->getPosition($data);
+
+        foreach ($messages->all($domain) as $source => $target) {
+            $resources .= pack('V', $this->getPosition($data));
+
+            $data .= pack('V', strlen($target))
+                .mb_convert_encoding($target."\0", 'UTF-16LE', 'UTF-8')
+                .$this->writePadding($data)
+                  ;
+        }
+
+        $resOffset = $this->getPosition($data);
+
+        $data .= pack('v', count($messages))
+            .$indexes
+            .$this->writePadding($data)
+            .$resources
+              ;
+
+        $bundleTop = $this->getPosition($data);
+
+        $root = pack('V7',
+            $resOffset + (2 << 28), // Resource Offset + Resource Type
+            6,                      // Index length
+            $keyTop,                // Index keys top
+            $bundleTop,             // Index resources top
+            $bundleTop,             // Index bundle top
+            count($messages),       // Index max table length
+            0                       // Index attributes
+        );
+
+        $header = pack('vC2v4C12@32',
+            32,                     // Header size
+            0xDA, 0x27,             // Magic number 1 and 2
+            20, 0, 0, 2,            // Rest of the header, ..., Size of a char
+            0x52, 0x65, 0x73, 0x42, // Data format identifier
+            1, 2, 0, 0,             // Data version
+            1, 4, 0, 0              // Unicode version
+        );
+
+        $output = $header
+               .$root
+               .$data;
+
+        return $output;
+    }
+
+    private function writePadding($data)
+    {
+        $padding = strlen($data) % 4;
+
+        if ($padding) {
+            return str_repeat("\xAA", 4 - $padding);
+        }
+    }
+
+    private function getPosition($data)
+    {
+        $position = (strlen($data) + 28) / 4;
+
+        return $position;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected function getExtension()
+    {
+        return 'res';
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Dumper/IniFileDumper.php b/vendor/symfony/translation/Symfony/Component/Translation/Dumper/IniFileDumper.php
new file mode 100644 (file)
index 0000000..173cf8c
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * IniFileDumper generates an ini formatted string representation of a message catalogue.
+ *
+ * @author Stealth35
+ */
+class IniFileDumper extends FileDumper
+{
+    /**
+     * {@inheritDoc}
+     */
+    public function format(MessageCatalogue $messages, $domain = 'messages')
+    {
+        $output = '';
+
+        foreach ($messages->all($domain) as $source => $target) {
+            $escapeTarget = str_replace('"', '\"', $target);
+            $output .= $source.'="'.$escapeTarget."\"\n";
+        }
+
+        return $output;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected function getExtension()
+    {
+        return 'ini';
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Dumper/MoFileDumper.php b/vendor/symfony/translation/Symfony/Component/Translation/Dumper/MoFileDumper.php
new file mode 100644 (file)
index 0000000..a3a2a64
--- /dev/null
@@ -0,0 +1,82 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Loader\MoFileLoader;
+
+/**
+ * MoFileDumper generates a gettext formatted string representation of a message catalogue.
+ *
+ * @author Stealth35
+ */
+class MoFileDumper extends FileDumper
+{
+    /**
+     * {@inheritDoc}
+     */
+    public function format(MessageCatalogue $messages, $domain = 'messages')
+    {
+        $output = $sources = $targets = $sourceOffsets = $targetOffsets = '';
+        $offsets = array();
+        $size = 0;
+
+        foreach ($messages->all($domain) as $source => $target) {
+            $offsets[] = array_map('strlen', array($sources, $source, $targets, $target));
+            $sources .= "\0".$source;
+            $targets .= "\0".$target;
+            ++$size;
+        }
+
+        $header = array(
+            'magicNumber'      => MoFileLoader::MO_LITTLE_ENDIAN_MAGIC,
+            'formatRevision'   => 0,
+            'count'            => $size,
+            'offsetId'         => MoFileLoader::MO_HEADER_SIZE,
+            'offsetTranslated' => MoFileLoader::MO_HEADER_SIZE + (8 * $size),
+            'sizeHashes'       => 0,
+            'offsetHashes'     => MoFileLoader::MO_HEADER_SIZE + (16 * $size),
+        );
+
+        $sourcesSize  = strlen($sources);
+        $sourcesStart = $header['offsetHashes'] + 1;
+
+        foreach ($offsets as $offset) {
+            $sourceOffsets .= $this->writeLong($offset[1])
+                          .$this->writeLong($offset[0] + $sourcesStart);
+            $targetOffsets .= $this->writeLong($offset[3])
+                          .$this->writeLong($offset[2] + $sourcesStart + $sourcesSize);
+        }
+
+        $output = implode(array_map(array($this, 'writeLong'), $header))
+               .$sourceOffsets
+               .$targetOffsets
+               .$sources
+               .$targets
+                ;
+
+        return $output;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected function getExtension()
+    {
+        return 'mo';
+    }
+
+    private function writeLong($str)
+    {
+        return pack('V*', $str);
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Dumper/PhpFileDumper.php b/vendor/symfony/translation/Symfony/Component/Translation/Dumper/PhpFileDumper.php
new file mode 100644 (file)
index 0000000..8de4a5b
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * PhpFileDumper generates php files from a message catalogue.
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+class PhpFileDumper extends FileDumper
+{
+    /**
+     * {@inheritDoc}
+     */
+    protected function format(MessageCatalogue $messages, $domain)
+    {
+        $output = "<?php\n\nreturn ".var_export($messages->all($domain), true).";\n";
+
+        return $output;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected function getExtension()
+    {
+        return 'php';
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Dumper/PoFileDumper.php b/vendor/symfony/translation/Symfony/Component/Translation/Dumper/PoFileDumper.php
new file mode 100644 (file)
index 0000000..d957ab9
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * PoFileDumper generates a gettext formatted string representation of a message catalogue.
+ *
+ * @author Stealth35
+ */
+class PoFileDumper extends FileDumper
+{
+    /**
+     * {@inheritDoc}
+     */
+    public function format(MessageCatalogue $messages, $domain = 'messages')
+    {
+        $output = '';
+        $newLine = false;
+        foreach ($messages->all($domain) as $source => $target) {
+            if ($newLine) {
+              $output .= "\n";
+            } else {
+              $newLine = true;
+            }
+            $output .= sprintf('msgid "%s"'."\n", $this->escape($source));
+            $output .= sprintf('msgstr "%s"', $this->escape($target));
+        }
+
+        return $output;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected function getExtension()
+    {
+        return 'po';
+    }
+
+    private function escape($str)
+    {
+        return addcslashes($str, "\0..\37\42\134");
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Dumper/QtFileDumper.php b/vendor/symfony/translation/Symfony/Component/Translation/Dumper/QtFileDumper.php
new file mode 100644 (file)
index 0000000..a1a8480
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * QtFileDumper generates ts files from a message catalogue.
+ *
+ * @author Benjamin Eberlei <kontakt@beberlei.de>
+ */
+class QtFileDumper extends FileDumper
+{
+    /**
+     * {@inheritDoc}
+     */
+    public function format(MessageCatalogue $messages, $domain)
+    {
+        $dom = new \DOMDocument('1.0', 'utf-8');
+        $dom->formatOutput = true;
+        $ts = $dom->appendChild($dom->createElement('TS'));
+        $context = $ts->appendChild($dom->createElement('context'));
+        $context->appendChild($dom->createElement('name', $domain));
+
+        foreach ($messages->all($domain) as $source => $target) {
+            $message = $context->appendChild($dom->createElement('message'));
+            $message->appendChild($dom->createElement('source', $source));
+            $message->appendChild($dom->createElement('translation', $target));
+        }
+
+        return $dom->saveXML();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected function getExtension()
+    {
+        return 'ts';
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Dumper/XliffFileDumper.php b/vendor/symfony/translation/Symfony/Component/Translation/Dumper/XliffFileDumper.php
new file mode 100644 (file)
index 0000000..0d258aa
--- /dev/null
@@ -0,0 +1,66 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * XliffFileDumper generates xliff files from a message catalogue.
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+class XliffFileDumper extends FileDumper
+{
+    /**
+     * {@inheritDoc}
+     */
+    protected function format(MessageCatalogue $messages, $domain)
+    {
+        $dom = new \DOMDocument('1.0', 'utf-8');
+        $dom->formatOutput = true;
+
+        $xliff = $dom->appendChild($dom->createElement('xliff'));
+        $xliff->setAttribute('version', '1.2');
+        $xliff->setAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:1.2');
+
+        $xliffFile = $xliff->appendChild($dom->createElement('file'));
+        $xliffFile->setAttribute('source-language', $messages->getLocale());
+        $xliffFile->setAttribute('datatype', 'plaintext');
+        $xliffFile->setAttribute('original', 'file.ext');
+
+        $xliffBody = $xliffFile->appendChild($dom->createElement('body'));
+        foreach ($messages->all($domain) as $source => $target) {
+            $translation = $dom->createElement('trans-unit');
+
+            $translation->setAttribute('id', md5($source));
+            $translation->setAttribute('resname', $source);
+
+            $s = $translation->appendChild($dom->createElement('source'));
+            $s->appendChild($dom->createTextNode($source));
+
+            $t = $translation->appendChild($dom->createElement('target'));
+            $t->appendChild($dom->createTextNode($target));
+
+            $xliffBody->appendChild($translation);
+        }
+
+        return $dom->saveXML();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected function getExtension()
+    {
+        return 'xlf';
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Dumper/YamlFileDumper.php b/vendor/symfony/translation/Symfony/Component/Translation/Dumper/YamlFileDumper.php
new file mode 100644 (file)
index 0000000..d8072fb
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Yaml\Yaml;
+
+/**
+ * YamlFileDumper generates yaml files from a message catalogue.
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+class YamlFileDumper extends FileDumper
+{
+    /**
+     * {@inheritDoc}
+     */
+    protected function format(MessageCatalogue $messages, $domain)
+    {
+         return Yaml::dump($messages->all($domain));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected function getExtension()
+    {
+        return 'yml';
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Exception/ExceptionInterface.php b/vendor/symfony/translation/Symfony/Component/Translation/Exception/ExceptionInterface.php
new file mode 100644 (file)
index 0000000..7757e66
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Exception;
+
+/**
+ * Exception interface for all exceptions thrown by the component.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+interface ExceptionInterface
+{
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Exception/InvalidResourceException.php b/vendor/symfony/translation/Symfony/Component/Translation/Exception/InvalidResourceException.php
new file mode 100644 (file)
index 0000000..6413f1a
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Exception;
+
+/**
+ * Thrown when a resource cannot be loaded.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class InvalidResourceException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Exception/NotFoundResourceException.php b/vendor/symfony/translation/Symfony/Component/Translation/Exception/NotFoundResourceException.php
new file mode 100644 (file)
index 0000000..7826e5c
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Exception;
+
+/**
+ * Thrown when a resource does not exist.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class NotFoundResourceException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Extractor/ChainExtractor.php b/vendor/symfony/translation/Symfony/Component/Translation/Extractor/ChainExtractor.php
new file mode 100644 (file)
index 0000000..0d07a93
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Extractor;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * ChainExtractor extracts translation messages from template files.
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+class ChainExtractor implements ExtractorInterface
+{
+    /**
+     * The extractors.
+     *
+     * @var ExtractorInterface[]
+     */
+    private $extractors = array();
+
+    /**
+     * Adds a loader to the translation extractor.
+     *
+     * @param string             $format    The format of the loader
+     * @param ExtractorInterface $extractor The loader
+     */
+    public function addExtractor($format, ExtractorInterface $extractor)
+    {
+        $this->extractors[$format] = $extractor;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function setPrefix($prefix)
+    {
+        foreach ($this->extractors as $extractor) {
+            $extractor->setPrefix($prefix);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function extract($directory, MessageCatalogue $catalogue)
+    {
+        foreach ($this->extractors as $extractor) {
+            $extractor->extract($directory, $catalogue);
+        }
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Extractor/ExtractorInterface.php b/vendor/symfony/translation/Symfony/Component/Translation/Extractor/ExtractorInterface.php
new file mode 100644 (file)
index 0000000..6f877c3
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Extractor;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * Extracts translation messages from a template directory to the catalogue.
+ * New found messages are injected to the catalogue using the prefix.
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+interface ExtractorInterface
+{
+    /**
+     * Extracts translation messages from a template directory to the catalogue.
+     *
+     * @param string           $directory The path to look into
+     * @param MessageCatalogue $catalogue The catalogue
+     */
+    public function extract($directory, MessageCatalogue $catalogue);
+
+    /**
+     * Sets the prefix that should be used for new found messages.
+     *
+     * @param string $prefix The prefix
+     */
+    public function setPrefix($prefix);
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/IdentityTranslator.php b/vendor/symfony/translation/Symfony/Component/Translation/IdentityTranslator.php
new file mode 100644 (file)
index 0000000..f30556b
--- /dev/null
@@ -0,0 +1,74 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+/**
+ * IdentityTranslator does not translate anything.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class IdentityTranslator implements TranslatorInterface
+{
+    private $selector;
+
+    /**
+     * Constructor.
+     *
+     * @param MessageSelector $selector The message selector for pluralization
+     *
+     * @api
+     */
+    public function __construct(MessageSelector $selector)
+    {
+        $this->selector = $selector;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function setLocale($locale)
+    {
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function getLocale()
+    {
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function trans($id, array $parameters = array(), $domain = 'messages', $locale = null)
+    {
+        return strtr((string) $id, $parameters);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function transChoice($id, $number, array $parameters = array(), $domain = 'messages', $locale = null)
+    {
+        return strtr($this->selector->choose((string) $id, (int) $number, $locale), $parameters);
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Interval.php b/vendor/symfony/translation/Symfony/Component/Translation/Interval.php
new file mode 100644 (file)
index 0000000..033b018
--- /dev/null
@@ -0,0 +1,107 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+/**
+ * Tests if a given number belongs to a given math interval.
+ *
+ * An interval can represent a finite set of numbers:
+ *
+ *  {1,2,3,4}
+ *
+ * An interval can represent numbers between two numbers:
+ *
+ *  [1, +Inf]
+ *  ]-1,2[
+ *
+ * The left delimiter can be [ (inclusive) or ] (exclusive).
+ * The right delimiter can be [ (exclusive) or ] (inclusive).
+ * Beside numbers, you can use -Inf and +Inf for the infinite.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @see    http://en.wikipedia.org/wiki/Interval_%28mathematics%29#The_ISO_notation
+ */
+class Interval
+{
+    /**
+     * Tests if the given number is in the math interval.
+     *
+     * @param integer $number   A number
+     * @param string  $interval An interval
+     *
+     * @return Boolean
+     *
+     * @throws \InvalidArgumentException
+     */
+    public static function test($number, $interval)
+    {
+        $interval = trim($interval);
+
+        if (!preg_match('/^'.self::getIntervalRegexp().'$/x', $interval, $matches)) {
+            throw new \InvalidArgumentException(sprintf('"%s" is not a valid interval.', $interval));
+        }
+
+        if ($matches[1]) {
+            foreach (explode(',', $matches[2]) as $n) {
+                if ($number == $n) {
+                    return true;
+                }
+            }
+        } else {
+            $leftNumber = self::convertNumber($matches['left']);
+            $rightNumber = self::convertNumber($matches['right']);
+
+            return
+                ('[' === $matches['left_delimiter'] ? $number >= $leftNumber : $number > $leftNumber)
+                && (']' === $matches['right_delimiter'] ? $number <= $rightNumber : $number < $rightNumber)
+            ;
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns a Regexp that matches valid intervals.
+     *
+     * @return string A Regexp (without the delimiters)
+     */
+    public static function getIntervalRegexp()
+    {
+        return <<<EOF
+        ({\s*
+            (\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*)
+        \s*})
+
+            |
+
+        (?P<left_delimiter>[\[\]])
+            \s*
+            (?P<left>-Inf|\-?\d+(\.\d+)?)
+            \s*,\s*
+            (?P<right>\+?Inf|\-?\d+(\.\d+)?)
+            \s*
+        (?P<right_delimiter>[\[\]])
+EOF;
+    }
+
+    private static function convertNumber($number)
+    {
+        if ('-Inf' === $number) {
+            return log(0);
+        } elseif ('+Inf' === $number || 'Inf' === $number) {
+            return -log(0);
+        }
+
+        return (float) $number;
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/LICENSE b/vendor/symfony/translation/Symfony/Component/Translation/LICENSE
new file mode 100644 (file)
index 0000000..88a57f8
--- /dev/null
@@ -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/translation/Symfony/Component/Translation/Loader/ArrayLoader.php b/vendor/symfony/translation/Symfony/Component/Translation/Loader/ArrayLoader.php
new file mode 100644 (file)
index 0000000..99058fb
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * ArrayLoader loads translations from a PHP array.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class ArrayLoader implements LoaderInterface
+{
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function load($resource, $locale, $domain = 'messages')
+    {
+        $this->flatten($resource);
+        $catalogue = new MessageCatalogue($locale);
+        $catalogue->add($resource, $domain);
+
+        return $catalogue;
+    }
+
+    /**
+     * Flattens an nested array of translations
+     *
+     * The scheme used is:
+     *   'key' => array('key2' => array('key3' => 'value'))
+     * Becomes:
+     *   'key.key2.key3' => 'value'
+     *
+     * This function takes an array by reference and will modify it
+     *
+     * @param array  &$messages The array that will be flattened
+     * @param array  $subnode Current subnode being parsed, used internally for recursive calls
+     * @param string $path    Current path being parsed, used internally for recursive calls
+     */
+    private function flatten(array &$messages, array $subnode = null, $path = null)
+    {
+        if (null === $subnode) {
+            $subnode =& $messages;
+        }
+        foreach ($subnode as $key => $value) {
+            if (is_array($value)) {
+                $nodePath = $path ? $path.'.'.$key : $key;
+                $this->flatten($messages, $value, $nodePath);
+                if (null === $path) {
+                    unset($messages[$key]);
+                }
+            } elseif (null !== $path) {
+                $messages[$path.'.'.$key] = $value;
+            }
+        }
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Loader/CsvFileLoader.php b/vendor/symfony/translation/Symfony/Component/Translation/Loader/CsvFileLoader.php
new file mode 100644 (file)
index 0000000..bc10188
--- /dev/null
@@ -0,0 +1,92 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Config\Resource\FileResource;
+
+/**
+ * CsvFileLoader loads translations from CSV files.
+ *
+ * @author Saša Stamenković <umpirsky@gmail.com>
+ *
+ * @api
+ */
+class CsvFileLoader extends ArrayLoader implements LoaderInterface
+{
+    private $delimiter = ';';
+    private $enclosure = '"';
+    private $escape    = '\\';
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function load($resource, $locale, $domain = 'messages')
+    {
+        if (!stream_is_local($resource)) {
+            throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+        }
+
+        if (!file_exists($resource)) {
+            throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+        }
+
+        $messages = array();
+
+        try {
+            $file = new \SplFileObject($resource, 'rb');
+        } catch (\RuntimeException $e) {
+            throw new NotFoundResourceException(sprintf('Error opening file "%s".', $resource), 0, $e);
+        }
+
+        $file->setFlags(\SplFileObject::READ_CSV | \SplFileObject::SKIP_EMPTY);
+        $file->setCsvControl($this->delimiter, $this->enclosure, $this->escape);
+
+        foreach ($file as $data) {
+            if (substr($data[0], 0, 1) === '#') {
+                continue;
+            }
+
+            if (!isset($data[1])) {
+                continue;
+            }
+
+            if (count($data) == 2) {
+                $messages[$data[0]] = $data[1];
+            } else {
+                 continue;
+            }
+        }
+
+        $catalogue = parent::load($messages, $locale, $domain);
+        $catalogue->addResource(new FileResource($resource));
+
+        return $catalogue;
+    }
+
+    /**
+     * Sets the delimiter, enclosure, and escape character for CSV.
+     *
+     * @param string $delimiter delimiter character
+     * @param string $enclosure enclosure character
+     * @param string $escape    escape character
+     */
+    public function setCsvControl($delimiter = ';', $enclosure = '"', $escape = '\\')
+    {
+        $this->delimiter = $delimiter;
+        $this->enclosure = $enclosure;
+        $this->escape    = $escape;
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Loader/IcuDatFileLoader.php b/vendor/symfony/translation/Symfony/Component/Translation/Loader/IcuDatFileLoader.php
new file mode 100644 (file)
index 0000000..104883b
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Config\Resource\FileResource;
+
+/**
+ * IcuResFileLoader loads translations from a resource bundle.
+ *
+ * @author stealth35
+ */
+class IcuDatFileLoader extends IcuResFileLoader
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function load($resource, $locale, $domain = 'messages')
+    {
+        if (!stream_is_local($resource.'.dat')) {
+            throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+        }
+
+        if (!file_exists($resource.'.dat')) {
+            throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+        }
+
+        $rb = new \ResourceBundle($locale, $resource);
+
+        if (!$rb) {
+            throw new InvalidResourceException(sprintf('Cannot load resource "%s"', $resource));
+        } elseif (intl_is_failure($rb->getErrorCode())) {
+            throw new InvalidResourceException($rb->getErrorMessage(), $rb->getErrorCode());
+        }
+
+        $messages = $this->flatten($rb);
+        $catalogue = new MessageCatalogue($locale);
+        $catalogue->add($messages, $domain);
+        $catalogue->addResource(new FileResource($resource.'.dat'));
+
+        return $catalogue;
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Loader/IcuResFileLoader.php b/vendor/symfony/translation/Symfony/Component/Translation/Loader/IcuResFileLoader.php
new file mode 100644 (file)
index 0000000..81b8e0f
--- /dev/null
@@ -0,0 +1,84 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Config\Resource\DirectoryResource;
+
+/**
+ * IcuResFileLoader loads translations from a resource bundle.
+ *
+ * @author stealth35
+ */
+class IcuResFileLoader implements LoaderInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function load($resource, $locale, $domain = 'messages')
+    {
+        if (!stream_is_local($resource)) {
+            throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+        }
+
+        if (!is_dir($resource)) {
+            throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+        }
+
+        $rb = new \ResourceBundle($locale, $resource);
+
+        if (!$rb) {
+            throw new InvalidResourceException(sprintf('Cannot load resource "%s"', $resource));
+        } elseif (intl_is_failure($rb->getErrorCode())) {
+            throw new InvalidResourceException($rb->getErrorMessage(), $rb->getErrorCode());
+        }
+
+        $messages = $this->flatten($rb);
+        $catalogue = new MessageCatalogue($locale);
+        $catalogue->add($messages, $domain);
+        $catalogue->addResource(new DirectoryResource($resource));
+
+        return $catalogue;
+    }
+
+    /**
+     * Flattens an ResourceBundle
+     *
+     * The scheme used is:
+     *   key { key2 { key3 { "value" } } }
+     * Becomes:
+     *   'key.key2.key3' => 'value'
+     *
+     * This function takes an array by reference and will modify it
+     *
+     * @param \ResourceBundle $rb       the ResourceBundle that will be flattened
+     * @param array           $messages used internally for recursive calls
+     * @param string          $path     current path being parsed, used internally for recursive calls
+     *
+     * @return array the flattened ResourceBundle
+     */
+    protected function flatten(\ResourceBundle $rb, array &$messages = array(), $path = null)
+    {
+        foreach ($rb as $key => $value) {
+            $nodePath = $path ? $path.'.'.$key : $key;
+            if ($value instanceof \ResourceBundle) {
+                $this->flatten($value, $messages, $nodePath);
+            } else {
+                $messages[$nodePath] = $value;
+            }
+        }
+
+        return $messages;
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Loader/IniFileLoader.php b/vendor/symfony/translation/Symfony/Component/Translation/Loader/IniFileLoader.php
new file mode 100644 (file)
index 0000000..616fa7e
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Config\Resource\FileResource;
+
+/**
+ * IniFileLoader loads translations from an ini file.
+ *
+ * @author stealth35
+ */
+class IniFileLoader extends ArrayLoader implements LoaderInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function load($resource, $locale, $domain = 'messages')
+    {
+        if (!stream_is_local($resource)) {
+            throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+        }
+
+        if (!file_exists($resource)) {
+            throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+        }
+
+        $messages = parse_ini_file($resource, true);
+
+        $catalogue = parent::load($messages, $locale, $domain);
+        $catalogue->addResource(new FileResource($resource));
+
+        return $catalogue;
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Loader/LoaderInterface.php b/vendor/symfony/translation/Symfony/Component/Translation/Loader/LoaderInterface.php
new file mode 100644 (file)
index 0000000..4d9965c
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+
+/**
+ * LoaderInterface is the interface implemented by all translation loaders.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+interface LoaderInterface
+{
+    /**
+     * Loads a locale.
+     *
+     * @param mixed  $resource A resource
+     * @param string $locale   A locale
+     * @param string $domain   The domain
+     *
+     * @return MessageCatalogue A MessageCatalogue instance
+     *
+     * @api
+     *
+     * @throws NotFoundResourceException when the resource cannot be found
+     * @throws InvalidResourceException  when the resource cannot be loaded
+     */
+    public function load($resource, $locale, $domain = 'messages');
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Loader/MoFileLoader.php b/vendor/symfony/translation/Symfony/Component/Translation/Loader/MoFileLoader.php
new file mode 100644 (file)
index 0000000..9d1cacb
--- /dev/null
@@ -0,0 +1,179 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Config\Resource\FileResource;
+
+/**
+ * @copyright Copyright (c) 2010, Union of RAD http://union-of-rad.org (http://lithify.me/)
+ */
+class MoFileLoader extends ArrayLoader implements LoaderInterface
+{
+    /**
+     * Magic used for validating the format of a MO file as well as
+     * detecting if the machine used to create that file was little endian.
+     *
+     * @var float
+     */
+    const MO_LITTLE_ENDIAN_MAGIC = 0x950412de;
+
+    /**
+     * Magic used for validating the format of a MO file as well as
+     * detecting if the machine used to create that file was big endian.
+     *
+     * @var float
+     */
+    const MO_BIG_ENDIAN_MAGIC = 0xde120495;
+
+    /**
+     * The size of the header of a MO file in bytes.
+     *
+     * @var integer Number of bytes.
+     */
+    const MO_HEADER_SIZE = 28;
+
+    public function load($resource, $locale, $domain = 'messages')
+    {
+        if (!stream_is_local($resource)) {
+            throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+        }
+
+        if (!file_exists($resource)) {
+            throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+        }
+
+        $messages = $this->parse($resource);
+
+        // empty file
+        if (null === $messages) {
+            $messages = array();
+        }
+
+        // not an array
+        if (!is_array($messages)) {
+            throw new InvalidResourceException(sprintf('The file "%s" must contain a valid mo file.', $resource));
+        }
+
+        $catalogue = parent::load($messages, $locale, $domain);
+        $catalogue->addResource(new FileResource($resource));
+
+        return $catalogue;
+    }
+
+    /**
+     * Parses machine object (MO) format, independent of the machine's endian it
+     * was created on. Both 32bit and 64bit systems are supported.
+     *
+     * @param resource $resource
+     *
+     * @return array
+     * @throws InvalidResourceException If stream content has an invalid format.
+     */
+    private function parse($resource)
+    {
+        $stream = fopen($resource, 'r');
+
+        $stat = fstat($stream);
+
+        if ($stat['size'] < self::MO_HEADER_SIZE) {
+            throw new InvalidResourceException("MO stream content has an invalid format.");
+        }
+        $magic = unpack('V1', fread($stream, 4));
+        $magic = hexdec(substr(dechex(current($magic)), -8));
+
+        if ($magic == self::MO_LITTLE_ENDIAN_MAGIC) {
+            $isBigEndian = false;
+        } elseif ($magic == self::MO_BIG_ENDIAN_MAGIC) {
+            $isBigEndian = true;
+        } else {
+            throw new InvalidResourceException("MO stream content has an invalid format.");
+        }
+
+        $formatRevision = $this->readLong($stream, $isBigEndian);
+        $count = $this->readLong($stream, $isBigEndian);
+        $offsetId = $this->readLong($stream, $isBigEndian);
+        $offsetTranslated = $this->readLong($stream, $isBigEndian);
+        $sizeHashes = $this->readLong($stream, $isBigEndian);
+        $offsetHashes = $this->readLong($stream, $isBigEndian);
+
+        $messages = array();
+
+        for ($i = 0; $i < $count; $i++) {
+            $singularId = $pluralId = null;
+            $translated = null;
+
+            fseek($stream, $offsetId + $i * 8);
+
+            $length = $this->readLong($stream, $isBigEndian);
+            $offset = $this->readLong($stream, $isBigEndian);
+
+            if ($length < 1) {
+                continue;
+            }
+
+            fseek($stream, $offset);
+            $singularId = fread($stream, $length);
+
+            if (strpos($singularId, "\000") !== false) {
+                list($singularId, $pluralId) = explode("\000", $singularId);
+            }
+
+            fseek($stream, $offsetTranslated + $i * 8);
+            $length = $this->readLong($stream, $isBigEndian);
+            $offset = $this->readLong($stream, $isBigEndian);
+
+            fseek($stream, $offset);
+            $translated = fread($stream, $length);
+
+            if (strpos($translated, "\000") !== false) {
+                $translated = explode("\000", $translated);
+            }
+
+            $ids = array('singular' => $singularId, 'plural' => $pluralId);
+            $item = compact('ids', 'translated');
+
+            if (is_array($item['translated'])) {
+                $messages[$item['ids']['singular']] = stripcslashes($item['translated'][0]);
+                if (isset($item['ids']['plural'])) {
+                    $plurals = array();
+                    foreach ($item['translated'] as $plural => $translated) {
+                        $plurals[] = sprintf('{%d} %s', $plural, $translated);
+                    }
+                    $messages[$item['ids']['plural']] = stripcslashes(implode('|', $plurals));
+                }
+            } elseif (!empty($item['ids']['singular'])) {
+                $messages[$item['ids']['singular']] = stripcslashes($item['translated']);
+            }
+        }
+
+        fclose($stream);
+
+        return array_filter($messages);
+    }
+
+    /**
+     * Reads an unsigned long from stream respecting endianess.
+     *
+     * @param  resource $stream
+     * @param  boolean  $isBigEndian
+     * @return integer
+     */
+    private function readLong($stream, $isBigEndian)
+    {
+        $result = unpack($isBigEndian ? 'N1' : 'V1', fread($stream, 4));
+        $result = current($result);
+
+        return (integer) substr($result, -8);
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Loader/PhpFileLoader.php b/vendor/symfony/translation/Symfony/Component/Translation/Loader/PhpFileLoader.php
new file mode 100644 (file)
index 0000000..4737d1b
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Config\Resource\FileResource;
+
+/**
+ * PhpFileLoader loads translations from PHP files returning an array of translations.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class PhpFileLoader extends ArrayLoader implements LoaderInterface
+{
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function load($resource, $locale, $domain = 'messages')
+    {
+        if (!stream_is_local($resource)) {
+            throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+        }
+
+        if (!file_exists($resource)) {
+            throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+        }
+
+        $messages = require($resource);
+
+        $catalogue = parent::load($messages, $locale, $domain);
+        $catalogue->addResource(new FileResource($resource));
+
+        return $catalogue;
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Loader/PoFileLoader.php b/vendor/symfony/translation/Symfony/Component/Translation/Loader/PoFileLoader.php
new file mode 100644 (file)
index 0000000..58ec6c7
--- /dev/null
@@ -0,0 +1,178 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Config\Resource\FileResource;
+
+/**
+ * @copyright Copyright (c) 2010, Union of RAD http://union-of-rad.org (http://lithify.me/)
+ * @copyright Copyright (c) 2012, Clemens Tolboom
+ */
+class PoFileLoader extends ArrayLoader implements LoaderInterface
+{
+    public function load($resource, $locale, $domain = 'messages')
+    {
+        if (!stream_is_local($resource)) {
+            throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+        }
+
+        if (!file_exists($resource)) {
+            throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+        }
+
+        $messages = $this->parse($resource);
+
+        // empty file
+        if (null === $messages) {
+            $messages = array();
+        }
+
+        // not an array
+        if (!is_array($messages)) {
+            throw new InvalidResourceException(sprintf('The file "%s" must contain a valid po file.', $resource));
+        }
+
+        $catalogue = parent::load($messages, $locale, $domain);
+        $catalogue->addResource(new FileResource($resource));
+
+        return $catalogue;
+    }
+
+    /**
+     * Parses portable object (PO) format.
+     *
+     * From http://www.gnu.org/software/gettext/manual/gettext.html#PO-Files
+     * we should be able to parse files having:
+     *
+     * white-space
+     * #  translator-comments
+     * #. extracted-comments
+     * #: reference...
+     * #, flag...
+     * #| msgid previous-untranslated-string
+     * msgid untranslated-string
+     * msgstr translated-string
+     *
+     * extra or different lines are:
+     *
+     * #| msgctxt previous-context
+     * #| msgid previous-untranslated-string
+     * msgctxt context
+     *
+     * #| msgid previous-untranslated-string-singular
+     * #| msgid_plural previous-untranslated-string-plural
+     * msgid untranslated-string-singular
+     * msgid_plural untranslated-string-plural
+     * msgstr[0] translated-string-case-0
+     * ...
+     * msgstr[N] translated-string-case-n
+     *
+     * The definition states:
+     * - white-space and comments are optional.
+     * - msgid "" that an empty singleline defines a header.
+     *
+     * This parser sacrifices some features of the reference implementation the
+     * differences to that implementation are as follows.
+     * - No support for comments spanning multiple lines.
+     * - Translator and extracted comments are treated as being the same type.
+     * - Message IDs are allowed to have other encodings as just US-ASCII.
+     *
+     * Items with an empty id are ignored.
+     *
+     * @param resource $resource
+     *
+     * @return array
+     */
+    private function parse($resource)
+    {
+        $stream = fopen($resource, 'r');
+
+        $defaults = array(
+            'ids' => array(),
+            'translated' => null,
+        );
+
+        $messages = array();
+        $item = $defaults;
+
+        while ($line = fgets($stream)) {
+            $line = trim($line);
+
+            if ($line === '') {
+                // Whitespace indicated current item is done
+                $this->addMessage($messages, $item);
+                $item = $defaults;
+            } elseif (substr($line, 0, 7) === 'msgid "') {
+                // We start a new msg so save previous
+                // TODO: this fails when comments or contexts are added
+                $this->addMessage($messages, $item);
+                $item = $defaults;
+                $item['ids']['singular'] = substr($line, 7, -1);
+            } elseif (substr($line, 0, 8) === 'msgstr "') {
+                $item['translated'] = substr($line, 8, -1);
+            } elseif ($line[0] === '"') {
+                $continues = isset($item['translated']) ? 'translated' : 'ids';
+
+                if (is_array($item[$continues])) {
+                    end($item[$continues]);
+                    $item[$continues][key($item[$continues])] .= substr($line, 1, -1);
+                } else {
+                    $item[$continues] .= substr($line, 1, -1);
+                }
+            } elseif (substr($line, 0, 14) === 'msgid_plural "') {
+                $item['ids']['plural'] = substr($line, 14, -1);
+            } elseif (substr($line, 0, 7) === 'msgstr[') {
+                $size = strpos($line, ']');
+                $item['translated'][(integer) substr($line, 7, 1)] = substr($line, $size + 3, -1);
+            }
+
+        }
+        // save last item
+        $this->addMessage($messages, $item);
+        fclose($stream);
+
+        return $messages;
+    }
+
+    /**
+     * Save a translation item to the messeages.
+     *
+     * A .po file could contain by error missing plural indexes. We need to
+     * fix these before saving them.
+     *
+     * @param array $messages
+     * @param array $item
+     */
+    private function addMessage(array &$messages, array $item)
+    {
+        if (is_array($item['translated'])) {
+            $messages[$item['ids']['singular']] = stripslashes($item['translated'][0]);
+            if (isset($item['ids']['plural'])) {
+                $plurals = $item['translated'];
+                // PO are by definition indexed so sort by index.
+                ksort($plurals);
+                // Make sure every index is filled.
+                end($plurals);
+                $count = key($plurals);
+                // Fill missing spots with '-'.
+                $empties = array_fill(0, $count+1, '-');
+                $plurals += $empties;
+                ksort($plurals);
+                $messages[$item['ids']['plural']] = stripcslashes(implode('|', $plurals));
+            }
+        } elseif (!empty($item['ids']['singular'])) {
+              $messages[$item['ids']['singular']] = stripslashes($item['translated']);
+        }
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Loader/QtFileLoader.php b/vendor/symfony/translation/Symfony/Component/Translation/Loader/QtFileLoader.php
new file mode 100644 (file)
index 0000000..d64494b
--- /dev/null
@@ -0,0 +1,95 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Config\Resource\FileResource;
+
+/**
+ * QtFileLoader loads translations from QT Translations XML files.
+ *
+ * @author Benjamin Eberlei <kontakt@beberlei.de>
+ *
+ * @api
+ */
+class QtFileLoader implements LoaderInterface
+{
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function load($resource, $locale, $domain = 'messages')
+    {
+        if (!stream_is_local($resource)) {
+            throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+        }
+
+        if (!file_exists($resource)) {
+            throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+        }
+
+        $dom = new \DOMDocument();
+        $current = libxml_use_internal_errors(true);
+        if (!@$dom->load($resource, defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0)) {
+            throw new InvalidResourceException(implode("\n", $this->getXmlErrors()));
+        }
+
+        $xpath = new \DOMXPath($dom);
+        $nodes = $xpath->evaluate('//TS/context/name[text()="'.$domain.'"]');
+
+        $catalogue = new MessageCatalogue($locale);
+        if ($nodes->length == 1) {
+            $translations = $nodes->item(0)->nextSibling->parentNode->parentNode->getElementsByTagName('message');
+            foreach ($translations as $translation) {
+                $catalogue->set(
+                    (string) $translation->getElementsByTagName('source')->item(0)->nodeValue,
+                    (string) $translation->getElementsByTagName('translation')->item(0)->nodeValue,
+                    $domain
+                );
+                $translation = $translation->nextSibling;
+            }
+            $catalogue->addResource(new FileResource($resource));
+        }
+
+        libxml_use_internal_errors($current);
+
+        return $catalogue;
+    }
+
+    /**
+     * Returns the XML errors of the internal XML parser
+     *
+     * @return array An array of errors
+     */
+    private function getXmlErrors()
+    {
+        $errors = array();
+        foreach (libxml_get_errors() as $error) {
+            $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
+                LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
+                $error->code,
+                trim($error->message),
+                $error->file ? $error->file : 'n/a',
+                $error->line,
+                $error->column
+            );
+        }
+
+        libxml_clear_errors();
+        libxml_use_internal_errors(false);
+
+        return $errors;
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Loader/XliffFileLoader.php b/vendor/symfony/translation/Symfony/Component/Translation/Loader/XliffFileLoader.php
new file mode 100644 (file)
index 0000000..0cafc04
--- /dev/null
@@ -0,0 +1,163 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Config\Resource\FileResource;
+
+/**
+ * XliffFileLoader loads translations from XLIFF files.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class XliffFileLoader implements LoaderInterface
+{
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function load($resource, $locale, $domain = 'messages')
+    {
+        if (!stream_is_local($resource)) {
+            throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+        }
+
+        if (!file_exists($resource)) {
+            throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+        }
+
+        list($xml, $encoding) = $this->parseFile($resource);
+        $xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:1.2');
+
+        $catalogue = new MessageCatalogue($locale);
+        foreach ($xml->xpath('//xliff:trans-unit') as $translation) {
+            $attributes = $translation->attributes();
+
+            if (!(isset($attributes['resname']) || isset($translation->source)) || !isset($translation->target)) {
+                continue;
+            }
+
+            $source = isset($attributes['resname']) && $attributes['resname'] ? $attributes['resname'] : $translation->source;
+            $target = (string) $translation->target;
+
+            // If the xlf file has another encoding specified, try to convert it because
+            // simple_xml will always return utf-8 encoded values
+            if ('UTF-8' !== $encoding && !empty($encoding)) {
+                if (function_exists('mb_convert_encoding')) {
+                    $target = mb_convert_encoding($target, $encoding, 'UTF-8');
+                } elseif (function_exists('iconv')) {
+                    $target = iconv('UTF-8', $encoding, $target);
+                } else {
+                    throw new \RuntimeException('No suitable convert encoding function (use UTF-8 as your encoding or install the iconv or mbstring extension).');
+                }
+            }
+
+            $catalogue->set((string) $source, $target, $domain);
+        }
+        $catalogue->addResource(new FileResource($resource));
+
+        return $catalogue;
+    }
+
+    /**
+     * Validates and parses the given file into a SimpleXMLElement
+     *
+     * @param string $file
+     *
+     * @throws \RuntimeException
+     *
+     * @return \SimpleXMLElement
+     *
+     * @throws InvalidResourceException
+     */
+    private function parseFile($file)
+    {
+        $internalErrors = libxml_use_internal_errors(true);
+        $disableEntities = libxml_disable_entity_loader(true);
+        libxml_clear_errors();
+
+        $dom = new \DOMDocument();
+        $dom->validateOnParse = true;
+        if (!@$dom->loadXML(file_get_contents($file), LIBXML_NONET | (defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0))) {
+            libxml_disable_entity_loader($disableEntities);
+
+            throw new InvalidResourceException(implode("\n", $this->getXmlErrors($internalErrors)));
+        }
+
+        libxml_disable_entity_loader($disableEntities);
+
+        foreach ($dom->childNodes as $child) {
+            if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
+                libxml_use_internal_errors($internalErrors);
+
+                throw new InvalidResourceException('Document types are not allowed.');
+            }
+        }
+
+        $location = str_replace('\\', '/', __DIR__).'/schema/dic/xliff-core/xml.xsd';
+        $parts = explode('/', $location);
+        if (0 === stripos($location, 'phar://')) {
+            $tmpfile = tempnam(sys_get_temp_dir(), 'sf2');
+            if ($tmpfile) {
+                copy($location, $tmpfile);
+                $parts = explode('/', str_replace('\\', '/', $tmpfile));
+            }
+        }
+        $drive = '\\' === DIRECTORY_SEPARATOR ? array_shift($parts).'/' : '';
+        $location = 'file:///'.$drive.implode('/', array_map('rawurlencode', $parts));
+
+        $source = file_get_contents(__DIR__.'/schema/dic/xliff-core/xliff-core-1.2-strict.xsd');
+        $source = str_replace('http://www.w3.org/2001/xml.xsd', $location, $source);
+
+        if (!@$dom->schemaValidateSource($source)) {
+            throw new InvalidResourceException(implode("\n", $this->getXmlErrors($internalErrors)));
+        }
+
+        $dom->normalizeDocument();
+
+        libxml_use_internal_errors($internalErrors);
+
+        return array(simplexml_import_dom($dom), strtoupper($dom->encoding));
+    }
+
+    /**
+     * Returns the XML errors of the internal XML parser
+     *
+     * @param Boolean $internalErrors
+     *
+     * @return array An array of errors
+     */
+    private function getXmlErrors($internalErrors)
+    {
+        $errors = array();
+        foreach (libxml_get_errors() as $error) {
+            $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
+                LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
+                $error->code,
+                trim($error->message),
+                $error->file ? $error->file : 'n/a',
+                $error->line,
+                $error->column
+            );
+        }
+
+        libxml_clear_errors();
+        libxml_use_internal_errors($internalErrors);
+
+        return $errors;
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Loader/YamlFileLoader.php b/vendor/symfony/translation/Symfony/Component/Translation/Loader/YamlFileLoader.php
new file mode 100644 (file)
index 0000000..66ab2ba
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Yaml\Parser as YamlParser;
+use Symfony\Component\Yaml\Exception\ParseException;
+
+/**
+ * YamlFileLoader loads translations from Yaml files.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class YamlFileLoader extends ArrayLoader implements LoaderInterface
+{
+    private $yamlParser;
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function load($resource, $locale, $domain = 'messages')
+    {
+        if (!stream_is_local($resource)) {
+            throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+        }
+
+        if (!file_exists($resource)) {
+            throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+        }
+
+        if (null === $this->yamlParser) {
+            $this->yamlParser = new YamlParser();
+        }
+
+        try {
+            $messages = $this->yamlParser->parse(file_get_contents($resource));
+        } catch (ParseException $e) {
+            throw new InvalidResourceException('Error parsing YAML.', 0, $e);
+        }
+
+        // empty file
+        if (null === $messages) {
+            $messages = array();
+        }
+
+        // not an array
+        if (!is_array($messages)) {
+            throw new InvalidResourceException(sprintf('The file "%s" must contain a YAML array.', $resource));
+        }
+
+        $catalogue = parent::load($messages, $locale, $domain);
+        $catalogue->addResource(new FileResource($resource));
+
+        return $catalogue;
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Loader/schema/dic/xliff-core/xliff-core-1.2-strict.xsd b/vendor/symfony/translation/Symfony/Component/Translation/Loader/schema/dic/xliff-core/xliff-core-1.2-strict.xsd
new file mode 100644 (file)
index 0000000..3ce2a8e
--- /dev/null
@@ -0,0 +1,2223 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+
+May-19-2004:
+- Changed the <choice> for ElemType_header, moving minOccurs="0" maxOccurs="unbounded" from its elements 
+to <choice> itself.
+- Added <choice> for ElemType_trans-unit to allow "any order" for <context-group>, <count-group>, <prop-group>, <note>, and
+<alt-trans>.
+
+Oct-2005
+- updated version info to 1.2
+- equiv-trans attribute to <trans-unit> element 
+- merged-trans attribute for <group> element
+- Add the <seg-source> element as optional in the <trans-unit> and <alt-trans> content models, at the same level as <source> 
+- Create a new value "seg" for the mtype attribute of the <mrk> element
+- Add mid as an optional attribute for the <alt-trans> element
+
+Nov-14-2005
+- Changed name attribute for <context-group> from required to optional
+- Added extension point at <xliff>
+
+Jan-9-2006
+- Added alttranstype type attribute to <alt-trans>, and values
+
+Jan-10-2006
+- Corrected error with overwritten purposeValueList
+- Corrected name="AttrType_Version",  attribute should have been "name"
+
+-->
+<xsd:schema xmlns:xlf="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="urn:oasis:names:tc:xliff:document:1.2" xml:lang="en">
+  <!-- Import for xml:lang and xml:space -->
+  <xsd:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+  <!-- Attributes Lists -->
+  <xsd:simpleType name="XTend">
+    <xsd:restriction base="xsd:string">
+      <xsd:pattern value="x-[^\s]+"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="context-typeValueList">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'context-type'.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:string">
+      <xsd:enumeration value="database">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a database content.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="element">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the content of an element within an XML document.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="elementtitle">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the name of an element within an XML document.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="linenumber">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the line number from the sourcefile (see context-type="sourcefile") where the &lt;source&gt; is found.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="numparams">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a the number of parameters contained within the &lt;source&gt;.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="paramnotes">
+        <xsd:annotation>
+          <xsd:documentation>Indicates notes pertaining to the parameters in the &lt;source&gt;.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="record">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the content of a record within a database.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="recordtitle">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the name of a record within a database.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="sourcefile">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the original source file in the case that multiple files are merged to form the original file from which the XLIFF file is created. This differs from the original &lt;file&gt; attribute in that this sourcefile is one of many that make up that file.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="count-typeValueList">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'count-type'.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="num-usages">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the count units are items that are used X times in a certain context; example: this is a reusable text unit which is used 42 times in other texts.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="repetition">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the count units are translation units existing already in the same document.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="total">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a total count.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="InlineDelimitersValueList">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'ctype' when used other elements than &lt;ph&gt; or &lt;x&gt;.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="bold">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a run of bolded text.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="italic">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a run of text in italics.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="underlined">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a run of underlined text.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="link">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a run of hyper-text.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="InlinePlaceholdersValueList">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'ctype' when used with &lt;ph&gt; or &lt;x&gt;.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="image">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a inline image.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="pb">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a page break.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="lb">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a line break.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="mime-typeValueList">
+    <xsd:restriction base="xsd:string">
+      <xsd:pattern value="(text|multipart|message|application|image|audio|video|model)(/.+)*"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="datatypeValueList">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'datatype'.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="asp">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Active Server Page data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="c">
+        <xsd:annotation>
+          <xsd:documentation>Indicates C source file data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="cdf">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Channel Definition Format (CDF) data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="cfm">
+        <xsd:annotation>
+          <xsd:documentation>Indicates ColdFusion data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="cpp">
+        <xsd:annotation>
+          <xsd:documentation>Indicates C++ source file data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="csharp">
+        <xsd:annotation>
+          <xsd:documentation>Indicates C-Sharp data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="cstring">
+        <xsd:annotation>
+          <xsd:documentation>Indicates strings from C, ASM, and driver files data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="csv">
+        <xsd:annotation>
+          <xsd:documentation>Indicates comma-separated values data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="database">
+        <xsd:annotation>
+          <xsd:documentation>Indicates database data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="documentfooter">
+        <xsd:annotation>
+          <xsd:documentation>Indicates portions of document that follows data and contains metadata.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="documentheader">
+        <xsd:annotation>
+          <xsd:documentation>Indicates portions of document that precedes data and contains metadata.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="filedialog">
+        <xsd:annotation>
+          <xsd:documentation>Indicates data from standard UI file operations dialogs (e.g., Open, Save, Save As, Export, Import).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="form">
+        <xsd:annotation>
+          <xsd:documentation>Indicates standard user input screen data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="html">
+        <xsd:annotation>
+          <xsd:documentation>Indicates HyperText Markup Language (HTML) data - document instance.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="htmlbody">
+        <xsd:annotation>
+          <xsd:documentation>Indicates content within an HTML document’s &lt;body&gt; element.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="ini">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Windows INI file data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="interleaf">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Interleaf data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="javaclass">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Java source file data (extension '.java').</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="javapropertyresourcebundle">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Java property resource bundle data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="javalistresourcebundle">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Java list resource bundle data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="javascript">
+        <xsd:annotation>
+          <xsd:documentation>Indicates JavaScript source file data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="jscript">
+        <xsd:annotation>
+          <xsd:documentation>Indicates JScript source file data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="layout">
+        <xsd:annotation>
+          <xsd:documentation>Indicates information relating to formatting.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="lisp">
+        <xsd:annotation>
+          <xsd:documentation>Indicates LISP source file data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="margin">
+        <xsd:annotation>
+          <xsd:documentation>Indicates information relating to margin formats.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="menufile">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a file containing menu.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="messagefile">
+        <xsd:annotation>
+          <xsd:documentation>Indicates numerically identified string table.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="mif">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Maker Interchange Format (MIF) data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="mimetype">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that the datatype attribute value is a MIME Type value and is defined in the mime-type attribute.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="mo">
+        <xsd:annotation>
+          <xsd:documentation>Indicates GNU Machine Object data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="msglib">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Message Librarian strings created by Novell's Message Librarian Tool.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="pagefooter">
+        <xsd:annotation>
+          <xsd:documentation>Indicates information to be displayed at the bottom of each page of a document.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="pageheader">
+        <xsd:annotation>
+          <xsd:documentation>Indicates information to be displayed at the top of each page of a document.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="parameters">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a list of property values (e.g., settings within INI files or preferences dialog).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="pascal">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Pascal source file data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="php">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Hypertext Preprocessor data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="plaintext">
+        <xsd:annotation>
+          <xsd:documentation>Indicates plain text file (no formatting other than, possibly, wrapping).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="po">
+        <xsd:annotation>
+          <xsd:documentation>Indicates GNU Portable Object file.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="report">
+        <xsd:annotation>
+          <xsd:documentation>Indicates dynamically generated user defined document. e.g. Oracle Report, Crystal Report, etc.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="resources">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Windows .NET binary resources.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="resx">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Windows .NET Resources.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="rtf">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Rich Text Format (RTF) data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="sgml">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Standard Generalized Markup Language (SGML) data - document instance.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="sgmldtd">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Standard Generalized Markup Language (SGML) data - Document Type Definition (DTD).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="svg">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Scalable Vector Graphic (SVG) data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="vbscript">
+        <xsd:annotation>
+          <xsd:documentation>Indicates VisualBasic Script source file.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="warning">
+        <xsd:annotation>
+          <xsd:documentation>Indicates warning message.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="winres">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Windows (Win32) resources (i.e. resources extracted from an RC script, a message file, or a compiled file).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="xhtml">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Extensible HyperText Markup Language (XHTML) data - document instance.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="xml">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Extensible Markup Language (XML) data - document instance.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="xmldtd">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Extensible Markup Language (XML) data - Document Type Definition (DTD).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="xsl">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Extensible Stylesheet Language (XSL) data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="xul">
+        <xsd:annotation>
+          <xsd:documentation>Indicates XUL elements.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="mtypeValueList">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'mtype'.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="abbrev">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the marked text is an abbreviation.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="abbreviated-form">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.8: A term resulting from the omission of any part of the full term while designating the same concept.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="abbreviation">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.8.1: An abbreviated form of a simple term resulting from the omission of some of its letters (e.g. 'adj.' for 'adjective').</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="acronym">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.8.4: An abbreviated form of a term made up of letters from the full form of a multiword term strung together into a sequence pronounced only syllabically (e.g. 'radar' for 'radio detecting and ranging').</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="appellation">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620: A proper-name term, such as the name of an agency or other proper entity.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="collocation">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.18.1: A recurrent word combination characterized by cohesion in that the components of the collocation must co-occur within an utterance or series of utterances, even though they do not necessarily have to maintain immediate proximity to one another.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="common-name">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.5: A synonym for an international scientific term that is used in general discourse in a given language.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="datetime">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the marked text is a date and/or time.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="equation">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.15: An expression used to represent a concept based on a statement that two mathematical expressions are, for instance, equal as identified by the equal sign (=), or assigned to one another by a similar sign.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="expanded-form">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.7: The complete representation of a term for which there is an abbreviated form.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="formula">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.14: Figures, symbols or the like used to express a concept briefly, such as a mathematical or chemical formula.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="head-term">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.1: The concept designation that has been chosen to head a terminological record.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="initialism">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.8.3: An abbreviated form of a term consisting of some of the initial letters of the words making up a multiword term or the term elements making up a compound term when these letters are pronounced individually (e.g. 'BSE' for 'bovine spongiform encephalopathy').</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="international-scientific-term">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.4: A term that is part of an international scientific nomenclature as adopted by an appropriate scientific body.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="internationalism">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.6: A term that has the same or nearly identical orthographic or phonemic form in many languages.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="logical-expression">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.16: An expression used to represent a concept based on mathematical or logical relations, such as statements of inequality, set relationships, Boolean operations, and the like.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="materials-management-unit">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.17: A unit to track object.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="name">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the marked text is a name.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="near-synonym">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.3: A term that represents the same or a very similar concept as another term in the same language, but for which interchangeability is limited to some contexts and inapplicable in others.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="part-number">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.17.2: A unique alphanumeric designation assigned to an object in a manufacturing system.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="phrase">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the marked text is a phrase.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="phraseological-unit">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.18: Any group of two or more words that form a unit, the meaning of which frequently cannot be deduced based on the combined sense of the words making up the phrase.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="protected">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the marked text should not be translated.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="romanized-form">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.12: A form of a term resulting from an operation whereby non-Latin writing systems are converted to the Latin alphabet.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="seg">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that the marked text represents a segment.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="set-phrase">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.18.2: A fixed, lexicalized phrase.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="short-form">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.8.2: A variant of a multiword term that includes fewer words than the full form of the term (e.g. 'Group of Twenty-four' for 'Intergovernmental Group of Twenty-four on International Monetary Affairs').</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="sku">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.17.1: Stock keeping unit, an inventory item identified by a unique alphanumeric designation assigned to an object in an inventory control system.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="standard-text">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.19: A fixed chunk of recurring text.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="symbol">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.13: A designation of a concept by letters, numerals, pictograms or any combination thereof.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="synonym">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.2: Any term that represents the same or a very similar concept as the main entry term in a term entry.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="synonymous-phrase">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.18.3: Phraseological unit in a language that expresses the same semantic content as another phrase in that same language.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="term">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the marked text is a term.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="transcribed-form">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.11: A form of a term resulting from an operation whereby the characters of one writing system are represented by characters from another writing system, taking into account the pronunciation of the characters converted.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="transliterated-form">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.10: A form of a term resulting from an operation whereby the characters of an alphabetic writing system are represented by characters from another alphabetic writing system.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="truncated-term">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.8.5: An abbreviated form of a term resulting from the omission of one or more term elements or syllables (e.g. 'flu' for 'influenza').</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="variant">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.9: One of the alternate forms of a term.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="restypeValueList">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'restype'.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="auto3state">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC AUTO3STATE control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="autocheckbox">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC AUTOCHECKBOX control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="autoradiobutton">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC AUTORADIOBUTTON control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="bedit">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC BEDIT control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="bitmap">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a bitmap, for example a BITMAP resource in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="button">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a button object, for example a BUTTON control Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="caption">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a caption, such as the caption of a dialog box.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="cell">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the cell in a table, for example the content of the &lt;td&gt; element in HTML.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="checkbox">
+        <xsd:annotation>
+          <xsd:documentation>Indicates check box object, for example a CHECKBOX control in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="checkboxmenuitem">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a menu item with an associated checkbox.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="checkedlistbox">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a list box, but with a check-box for each item.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="colorchooser">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a color selection dialog.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="combobox">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a combination of edit box and listbox object, for example a COMBOBOX control in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="comboboxexitem">
+        <xsd:annotation>
+          <xsd:documentation>Indicates an initialization entry of an extended combobox DLGINIT resource block. (code 0x1234).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="comboboxitem">
+        <xsd:annotation>
+          <xsd:documentation>Indicates an initialization entry of a combobox DLGINIT resource block (code 0x0403).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="component">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a UI base class element that cannot be represented by any other element.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="contextmenu">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a context menu.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="ctext">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC CTEXT control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="cursor">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a cursor, for example a CURSOR resource in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="datetimepicker">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a date/time picker.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="defpushbutton">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC DEFPUSHBUTTON control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="dialog">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a dialog box.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="dlginit">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC DLGINIT resource block.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="edit">
+        <xsd:annotation>
+          <xsd:documentation>Indicates an edit box object, for example an EDIT control in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="file">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a filename.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="filechooser">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a file dialog.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="fn">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a footnote.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="font">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a font name.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="footer">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a footer.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="frame">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a frame object.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="grid">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a XUL grid element.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="groupbox">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a groupbox object, for example a GROUPBOX control in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="header">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a header item.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="heading">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a heading, such has the content of &lt;h1&gt;, &lt;h2&gt;, etc. in HTML.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="hedit">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC HEDIT control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="hscrollbar">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a horizontal scrollbar.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="icon">
+        <xsd:annotation>
+          <xsd:documentation>Indicates an icon, for example an ICON resource in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="iedit">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC IEDIT control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="keywords">
+        <xsd:annotation>
+          <xsd:documentation>Indicates keyword list, such as the content of the Keywords meta-data in HTML, or a K footnote in WinHelp RTF.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="label">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a label object.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="linklabel">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a label that is also a HTML link (not necessarily a URL).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="list">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a list (a group of list-items, for example an &lt;ol&gt; or &lt;ul&gt; element in HTML).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="listbox">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a listbox object, for example an LISTBOX control in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="listitem">
+        <xsd:annotation>
+          <xsd:documentation>Indicates an list item (an entry in a list).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="ltext">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC LTEXT control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="menu">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a menu (a group of menu-items).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="menubar">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a toolbar containing one or more tope level menus.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="menuitem">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a menu item (an entry in a menu).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="menuseparator">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a XUL menuseparator element.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="message">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a message, for example an entry in a MESSAGETABLE resource in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="monthcalendar">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a calendar control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="numericupdown">
+        <xsd:annotation>
+          <xsd:documentation>Indicates an edit box beside a spin control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="panel">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a catch all for rectangular areas.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="popupmenu">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a standalone menu not necessarily associated with a menubar.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="pushbox">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a pushbox object, for example a PUSHBOX control in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="pushbutton">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC PUSHBUTTON control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="radio">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a radio button object.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="radiobuttonmenuitem">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a menuitem with associated radio button.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="rcdata">
+        <xsd:annotation>
+          <xsd:documentation>Indicates raw data resources for an application.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="row">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a row in a table.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="rtext">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC RTEXT control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="scrollpane">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a user navigable container used to show a portion of a document.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="separator">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a generic divider object (e.g. menu group separator).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="shortcut">
+        <xsd:annotation>
+          <xsd:documentation>Windows accelerators, shortcuts in resource or property files.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="spinner">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a UI control to indicate process activity but not progress.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="splitter">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a splitter bar.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="state3">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC STATE3 control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="statusbar">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a window for providing feedback to the users, like 'read-only', etc.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="string">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a string, for example an entry in a STRINGTABLE resource in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="tabcontrol">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a layers of controls with a tab to select layers.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="table">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a display and edits regular two-dimensional tables of cells.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="textbox">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a XUL textbox element.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="togglebutton">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a UI button that can be toggled to on or off state.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="toolbar">
+        <xsd:annotation>
+          <xsd:documentation>Indicates an array of controls, usually buttons.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="tooltip">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a pop up tool tip text.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="trackbar">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a bar with a pointer indicating a position within a certain range.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="tree">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a control that displays a set of hierarchical data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="uri">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a URI (URN or URL).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="userbutton">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC USERBUTTON control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="usercontrol">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a user-defined control like CONTROL control in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="var">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the text of a variable.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="versioninfo">
+        <xsd:annotation>
+          <xsd:documentation>Indicates version information about a resource like VERSIONINFO in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="vscrollbar">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a vertical scrollbar.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="window">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a graphical window.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="size-unitValueList">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'size-unit'.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="byte">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in 8-bit bytes.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="char">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in Unicode characters.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="col">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in columns. Used for HTML text area.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="cm">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in centimeters.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="dlgunit">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in dialog units, as defined in Windows resources.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="em">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in 'font-size' units (as defined in CSS).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="ex">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in 'x-height' units (as defined in CSS).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="glyph">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in glyphs. A glyph is considered to be one or more combined Unicode characters that represent a single displayable text character. Sometimes referred to as a 'grapheme cluster'</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="in">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in inches.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="mm">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in millimeters.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="percent">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in percentage.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="pixel">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in pixels.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="point">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in point.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="row">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in rows. Used for HTML text area.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="stateValueList">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'state'.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="final">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the terminating state.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="needs-adaptation">
+        <xsd:annotation>
+          <xsd:documentation>Indicates only non-textual information needs adaptation.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="needs-l10n">
+        <xsd:annotation>
+          <xsd:documentation>Indicates both text and non-textual information needs adaptation.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="needs-review-adaptation">
+        <xsd:annotation>
+          <xsd:documentation>Indicates only non-textual information needs review.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="needs-review-l10n">
+        <xsd:annotation>
+          <xsd:documentation>Indicates both text and non-textual information needs review.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="needs-review-translation">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that only the text of the item needs to be reviewed.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="needs-translation">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that the item needs to be translated.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="new">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that the item is new. For example, translation units that were not in a previous version of the document.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="signed-off">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that changes are reviewed and approved.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="translated">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that the item has been translated.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="state-qualifierValueList">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'state-qualifier'.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="exact-match">
+        <xsd:annotation>
+          <xsd:documentation>Indicates an exact match. An exact match occurs when a source text of a segment is exactly the same as the source text of a segment that was translated previously.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="fuzzy-match">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a fuzzy match. A fuzzy match occurs when a source text of a segment is very similar to the source text of a segment that was translated previously (e.g. when the difference is casing, a few changed words, white-space discripancy, etc.).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="id-match">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a match based on matching IDs (in addition to matching text).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="leveraged-glossary">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a translation derived from a glossary.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="leveraged-inherited">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a translation derived from existing translation.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="leveraged-mt">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a translation derived from machine translation.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="leveraged-repository">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a translation derived from a translation repository.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="leveraged-tm">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a translation derived from a translation memory.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="mt-suggestion">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the translation is suggested by machine translation.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="rejected-grammar">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that the item has been rejected because of incorrect grammar.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="rejected-inaccurate">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that the item has been rejected because it is incorrect.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="rejected-length">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that the item has been rejected because it is too long or too short.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="rejected-spelling">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that the item has been rejected because of incorrect spelling.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="tm-suggestion">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the translation is suggested by translation memory.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="unitValueList">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'unit'.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="word">
+        <xsd:annotation>
+          <xsd:documentation>Refers to words.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="page">
+        <xsd:annotation>
+          <xsd:documentation>Refers to pages.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="trans-unit">
+        <xsd:annotation>
+          <xsd:documentation>Refers to &lt;trans-unit&gt; elements.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="bin-unit">
+        <xsd:annotation>
+          <xsd:documentation>Refers to &lt;bin-unit&gt; elements.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="glyph">
+        <xsd:annotation>
+          <xsd:documentation>Refers to glyphs.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="item">
+        <xsd:annotation>
+          <xsd:documentation>Refers to &lt;trans-unit&gt; and/or &lt;bin-unit&gt; elements.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="instance">
+        <xsd:annotation>
+          <xsd:documentation>Refers to the occurrences of instances defined by the count-type value.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="character">
+        <xsd:annotation>
+          <xsd:documentation>Refers to characters.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="line">
+        <xsd:annotation>
+          <xsd:documentation>Refers to lines.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="sentence">
+        <xsd:annotation>
+          <xsd:documentation>Refers to sentences.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="paragraph">
+        <xsd:annotation>
+          <xsd:documentation>Refers to paragraphs.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="segment">
+        <xsd:annotation>
+          <xsd:documentation>Refers to segments.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="placeable">
+        <xsd:annotation>
+          <xsd:documentation>Refers to placeables (inline elements).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="priorityValueList">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'priority'.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:positiveInteger">
+      <xsd:enumeration value="1">
+        <xsd:annotation>
+          <xsd:documentation>Highest priority.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="2">
+        <xsd:annotation>
+          <xsd:documentation>High priority.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="3">
+        <xsd:annotation>
+          <xsd:documentation>High priority, but not as important as 2.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="4">
+        <xsd:annotation>
+          <xsd:documentation>High priority, but not as important as 3.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="5">
+        <xsd:annotation>
+          <xsd:documentation>Medium priority, but more important than 6.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="6">
+        <xsd:annotation>
+          <xsd:documentation>Medium priority, but less important than 5.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="7">
+        <xsd:annotation>
+          <xsd:documentation>Low priority, but more important than 8.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="8">
+        <xsd:annotation>
+          <xsd:documentation>Low priority, but more important than 9.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="9">
+        <xsd:annotation>
+          <xsd:documentation>Low priority.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="10">
+        <xsd:annotation>
+          <xsd:documentation>Lowest priority.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="reformatValueYesNo">
+    <xsd:restriction base="xsd:string">
+      <xsd:enumeration value="yes">
+        <xsd:annotation>
+          <xsd:documentation>This value indicates that all properties can be reformatted. This value must be used alone.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="no">
+        <xsd:annotation>
+          <xsd:documentation>This value indicates that no properties should be reformatted. This value must be used alone.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="reformatValueList">
+    <xsd:list>
+      <xsd:simpleType>
+        <xsd:union memberTypes="xlf:XTend">
+          <xsd:simpleType>
+            <xsd:restriction base="xsd:string">
+              <xsd:enumeration value="coord">
+                <xsd:annotation>
+                  <xsd:documentation>This value indicates that all information in the coord attribute can be modified.</xsd:documentation>
+                </xsd:annotation>
+              </xsd:enumeration>
+              <xsd:enumeration value="coord-x">
+                <xsd:annotation>
+                  <xsd:documentation>This value indicates that the x information in the coord attribute can be modified.</xsd:documentation>
+                </xsd:annotation>
+              </xsd:enumeration>
+              <xsd:enumeration value="coord-y">
+                <xsd:annotation>
+                  <xsd:documentation>This value indicates that the y information in the coord attribute can be modified.</xsd:documentation>
+                </xsd:annotation>
+              </xsd:enumeration>
+              <xsd:enumeration value="coord-cx">
+                <xsd:annotation>
+                  <xsd:documentation>This value indicates that the cx information in the coord attribute can be modified.</xsd:documentation>
+                </xsd:annotation>
+              </xsd:enumeration>
+              <xsd:enumeration value="coord-cy">
+                <xsd:annotation>
+                  <xsd:documentation>This value indicates that the cy information in the coord attribute can be modified.</xsd:documentation>
+                </xsd:annotation>
+              </xsd:enumeration>
+              <xsd:enumeration value="font">
+                <xsd:annotation>
+                  <xsd:documentation>This value indicates that all the information in the font attribute can be modified.</xsd:documentation>
+                </xsd:annotation>
+              </xsd:enumeration>
+              <xsd:enumeration value="font-name">
+                <xsd:annotation>
+                  <xsd:documentation>This value indicates that the name information in the font attribute can be modified.</xsd:documentation>
+                </xsd:annotation>
+              </xsd:enumeration>
+              <xsd:enumeration value="font-size">
+                <xsd:annotation>
+                  <xsd:documentation>This value indicates that the size information in the font attribute can be modified.</xsd:documentation>
+                </xsd:annotation>
+              </xsd:enumeration>
+              <xsd:enumeration value="font-weight">
+                <xsd:annotation>
+                  <xsd:documentation>This value indicates that the weight information in the font attribute can be modified.</xsd:documentation>
+                </xsd:annotation>
+              </xsd:enumeration>
+              <xsd:enumeration value="css-style">
+                <xsd:annotation>
+                  <xsd:documentation>This value indicates that the information in the css-style attribute can be modified.</xsd:documentation>
+                </xsd:annotation>
+              </xsd:enumeration>
+              <xsd:enumeration value="style">
+                <xsd:annotation>
+                  <xsd:documentation>This value indicates that the information in the style attribute can be modified.</xsd:documentation>
+                </xsd:annotation>
+              </xsd:enumeration>
+              <xsd:enumeration value="ex-style">
+                <xsd:annotation>
+                  <xsd:documentation>This value indicates that the information in the exstyle attribute can be modified.</xsd:documentation>
+                </xsd:annotation>
+              </xsd:enumeration>
+            </xsd:restriction>
+          </xsd:simpleType>
+        </xsd:union>
+      </xsd:simpleType>
+    </xsd:list>
+  </xsd:simpleType>
+  <xsd:simpleType name="purposeValueList">
+    <xsd:restriction base="xsd:string">
+      <xsd:enumeration value="information">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that the context is informational in nature, specifying for example, how a term should be translated. Thus, should be displayed to anyone editing the XLIFF document.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="location">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that the context-group is used to specify where the term was found in the translatable source. Thus, it is not displayed.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="match">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that the context information should be used during translation memory lookups. Thus, it is not displayed.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="alttranstypeValueList">
+    <xsd:restriction base="xsd:string">
+      <xsd:enumeration value="proposal">
+        <xsd:annotation>
+          <xsd:documentation>Represents a translation proposal from a translation memory or other resource.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="previous-version">
+        <xsd:annotation>
+          <xsd:documentation>Represents a previous version of the target element.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="rejected">
+        <xsd:annotation>
+          <xsd:documentation>Represents a rejected version of the target element.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="reference">
+        <xsd:annotation>
+          <xsd:documentation>Represents a translation to be used for reference purposes only, for example from a related product or a different language.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="accepted">
+        <xsd:annotation>
+          <xsd:documentation>Represents a proposed translation that was used for the translation of the trans-unit, possibly modified.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <!-- Other Types -->
+  <xsd:complexType name="ElemType_ExternalReference">
+    <xsd:choice>
+      <xsd:element ref="xlf:internal-file"/>
+      <xsd:element ref="xlf:external-file"/>
+    </xsd:choice>
+  </xsd:complexType>
+  <xsd:simpleType name="AttrType_purpose">
+    <xsd:list>
+      <xsd:simpleType>
+        <xsd:union memberTypes="xlf:purposeValueList xlf:XTend"/>
+      </xsd:simpleType>
+    </xsd:list>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_datatype">
+    <xsd:union memberTypes="xlf:datatypeValueList xlf:XTend"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_restype">
+    <xsd:union memberTypes="xlf:restypeValueList xlf:XTend"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_alttranstype">
+    <xsd:union memberTypes="xlf:alttranstypeValueList xlf:XTend"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_context-type">
+    <xsd:union memberTypes="xlf:context-typeValueList xlf:XTend"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_state">
+    <xsd:union memberTypes="xlf:stateValueList xlf:XTend"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_state-qualifier">
+    <xsd:union memberTypes="xlf:state-qualifierValueList xlf:XTend"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_count-type">
+    <xsd:union memberTypes="xlf:restypeValueList xlf:count-typeValueList xlf:datatypeValueList xlf:stateValueList xlf:state-qualifierValueList xlf:XTend"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_InlineDelimiters">
+    <xsd:union memberTypes="xlf:InlineDelimitersValueList xlf:XTend"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_InlinePlaceholders">
+    <xsd:union memberTypes="xlf:InlinePlaceholdersValueList xlf:XTend"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_size-unit">
+    <xsd:union memberTypes="xlf:size-unitValueList xlf:XTend"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_mtype">
+    <xsd:union memberTypes="xlf:mtypeValueList xlf:XTend"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_unit">
+    <xsd:union memberTypes="xlf:unitValueList xlf:XTend"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_priority">
+    <xsd:union memberTypes="xlf:priorityValueList"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_reformat">
+    <xsd:union memberTypes="xlf:reformatValueYesNo xlf:reformatValueList"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_YesNo">
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="yes"/>
+      <xsd:enumeration value="no"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_Position">
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="open"/>
+      <xsd:enumeration value="close"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_assoc">
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="preceding"/>
+      <xsd:enumeration value="following"/>
+      <xsd:enumeration value="both"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_annotates">
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="source"/>
+      <xsd:enumeration value="target"/>
+      <xsd:enumeration value="general"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_Coordinates">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'coord'.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:string">
+      <xsd:pattern value="(-?\d+|#);(-?\d+|#);(-?\d+|#);(-?\d+|#)"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_Version">
+    <xsd:annotation>
+      <xsd:documentation>Version values: 1.0 and 1.1 are allowed for backward compatibility.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:string">
+      <xsd:enumeration value="1.2"/>
+      <xsd:enumeration value="1.1"/>
+      <xsd:enumeration value="1.0"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <!-- Groups -->
+  <xsd:group name="ElemGroup_TextContent">
+    <xsd:choice>
+      <xsd:element ref="xlf:g"/>
+      <xsd:element ref="xlf:bpt"/>
+      <xsd:element ref="xlf:ept"/>
+      <xsd:element ref="xlf:ph"/>
+      <xsd:element ref="xlf:it"/>
+      <xsd:element ref="xlf:mrk"/>
+      <xsd:element ref="xlf:x"/>
+      <xsd:element ref="xlf:bx"/>
+      <xsd:element ref="xlf:ex"/>
+    </xsd:choice>
+  </xsd:group>
+  <xsd:attributeGroup name="AttrGroup_TextContent">
+    <xsd:attribute name="id" type="xsd:string" use="required"/>
+    <xsd:attribute name="xid" type="xsd:string" use="optional"/>
+    <xsd:attribute name="equiv-text" type="xsd:string" use="optional"/>
+    <xsd:anyAttribute namespace="##other" processContents="strict"/>
+  </xsd:attributeGroup>
+  <!-- XLIFF Structure -->
+  <xsd:element name="xliff">
+    <xsd:complexType>
+      <xsd:sequence maxOccurs="unbounded">
+        <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="strict"/>
+        <xsd:element ref="xlf:file"/>
+      </xsd:sequence>
+      <xsd:attribute name="version" type="xlf:AttrType_Version" use="required"/>
+      <xsd:attribute ref="xml:lang" use="optional"/>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="file">
+    <xsd:complexType>
+      <xsd:sequence>
+        <xsd:element minOccurs="0" ref="xlf:header"/>
+        <xsd:element ref="xlf:body"/>
+      </xsd:sequence>
+      <xsd:attribute name="original" type="xsd:string" use="required"/>
+      <xsd:attribute name="source-language" type="xsd:language" use="required"/>
+      <xsd:attribute name="datatype" type="xlf:AttrType_datatype" use="required"/>
+      <xsd:attribute name="tool-id" type="xsd:string" use="optional"/>
+      <xsd:attribute name="date" type="xsd:dateTime" use="optional"/>
+      <xsd:attribute ref="xml:space" use="optional"/>
+      <xsd:attribute name="category" type="xsd:string" use="optional"/>
+      <xsd:attribute name="target-language" type="xsd:language" use="optional"/>
+      <xsd:attribute name="product-name" type="xsd:string" use="optional"/>
+      <xsd:attribute name="product-version" type="xsd:string" use="optional"/>
+      <xsd:attribute name="build-num" type="xsd:string" use="optional"/>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+    <xsd:unique name="U_group_id">
+      <xsd:selector xpath=".//xlf:group"/>
+      <xsd:field xpath="@id"/>
+    </xsd:unique>
+    <xsd:key name="K_unit_id">
+      <xsd:selector xpath=".//xlf:trans-unit|.//xlf:bin-unit"/>
+      <xsd:field xpath="@id"/>
+    </xsd:key>
+    <xsd:keyref name="KR_unit_id" refer="xlf:K_unit_id">
+      <xsd:selector xpath=".//bpt|.//ept|.//it|.//ph|.//g|.//x|.//bx|.//ex|.//sub"/>
+      <xsd:field xpath="@xid"/>
+    </xsd:keyref>
+    <xsd:key name="K_tool-id">
+      <xsd:selector xpath="xlf:header/xlf:tool"/>
+      <xsd:field xpath="@tool-id"/>
+    </xsd:key>
+    <xsd:keyref name="KR_file_tool-id" refer="xlf:K_tool-id">
+      <xsd:selector xpath="."/>
+      <xsd:field xpath="@tool-id"/>
+    </xsd:keyref>
+    <xsd:keyref name="KR_phase_tool-id" refer="xlf:K_tool-id">
+      <xsd:selector xpath="xlf:header/xlf:phase-group/xlf:phase"/>
+      <xsd:field xpath="@tool-id"/>
+    </xsd:keyref>
+    <xsd:keyref name="KR_alt-trans_tool-id" refer="xlf:K_tool-id">
+      <xsd:selector xpath=".//xlf:trans-unit/xlf:alt-trans"/>
+      <xsd:field xpath="@tool-id"/>
+    </xsd:keyref>
+    <xsd:key name="K_count-group_name">
+      <xsd:selector xpath=".//xlf:count-group"/>
+      <xsd:field xpath="@name"/>
+    </xsd:key>
+    <xsd:unique name="U_context-group_name">
+      <xsd:selector xpath=".//xlf:context-group"/>
+      <xsd:field xpath="@name"/>
+    </xsd:unique>
+    <xsd:key name="K_phase-name">
+      <xsd:selector xpath="xlf:header/xlf:phase-group/xlf:phase"/>
+      <xsd:field xpath="@phase-name"/>
+    </xsd:key>
+    <xsd:keyref name="KR_phase-name" refer="xlf:K_phase-name">
+      <xsd:selector xpath=".//xlf:count|.//xlf:trans-unit|.//xlf:target|.//bin-unit|.//bin-target"/>
+      <xsd:field xpath="@phase-name"/>
+    </xsd:keyref>
+    <xsd:unique name="U_uid">
+      <xsd:selector xpath=".//xlf:external-file"/>
+      <xsd:field xpath="@uid"/>
+    </xsd:unique>
+  </xsd:element>
+  <xsd:element name="header">
+    <xsd:complexType>
+      <xsd:sequence>
+        <xsd:element minOccurs="0" name="skl" type="xlf:ElemType_ExternalReference"/>
+        <xsd:element minOccurs="0" ref="xlf:phase-group"/>
+        <xsd:choice maxOccurs="unbounded" minOccurs="0">
+          <xsd:element name="glossary" type="xlf:ElemType_ExternalReference"/>
+          <xsd:element name="reference" type="xlf:ElemType_ExternalReference"/>
+          <xsd:element ref="xlf:count-group"/>
+          <xsd:element ref="xlf:note"/>
+          <xsd:element ref="xlf:tool"/>
+        </xsd:choice>
+        <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="strict"/>
+      </xsd:sequence>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="internal-file">
+    <xsd:complexType>
+      <xsd:simpleContent>
+        <xsd:extension base="xsd:string">
+          <xsd:attribute name="form" type="xsd:string"/>
+          <xsd:attribute name="crc" type="xsd:NMTOKEN"/>
+        </xsd:extension>
+      </xsd:simpleContent>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="external-file">
+    <xsd:complexType>
+      <xsd:attribute name="href" type="xsd:string" use="required"/>
+      <xsd:attribute name="crc" type="xsd:NMTOKEN"/>
+      <xsd:attribute name="uid" type="xsd:NMTOKEN"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="note">
+    <xsd:complexType>
+      <xsd:simpleContent>
+        <xsd:extension base="xsd:string">
+          <xsd:attribute ref="xml:lang" use="optional"/>
+          <xsd:attribute default="1" name="priority" type="xlf:AttrType_priority" use="optional"/>
+          <xsd:attribute name="from" type="xsd:string" use="optional"/>
+          <xsd:attribute default="general" name="annotates" type="xlf:AttrType_annotates" use="optional"/>
+        </xsd:extension>
+      </xsd:simpleContent>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="phase-group">
+    <xsd:complexType>
+      <xsd:sequence maxOccurs="unbounded">
+        <xsd:element ref="xlf:phase"/>
+      </xsd:sequence>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="phase">
+    <xsd:complexType>
+      <xsd:sequence maxOccurs="unbounded" minOccurs="0">
+        <xsd:element ref="xlf:note"/>
+      </xsd:sequence>
+      <xsd:attribute name="phase-name" type="xsd:string" use="required"/>
+      <xsd:attribute name="process-name" type="xsd:string" use="required"/>
+      <xsd:attribute name="company-name" type="xsd:string" use="optional"/>
+      <xsd:attribute name="tool-id" type="xsd:string" use="optional"/>
+      <xsd:attribute name="date" type="xsd:dateTime" use="optional"/>
+      <xsd:attribute name="job-id" type="xsd:string" use="optional"/>
+      <xsd:attribute name="contact-name" type="xsd:string" use="optional"/>
+      <xsd:attribute name="contact-email" type="xsd:string" use="optional"/>
+      <xsd:attribute name="contact-phone" type="xsd:string" use="optional"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="count-group">
+    <xsd:complexType>
+      <xsd:sequence maxOccurs="unbounded" minOccurs="0">
+        <xsd:element ref="xlf:count"/>
+      </xsd:sequence>
+      <xsd:attribute name="name" type="xsd:string" use="required"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="count">
+    <xsd:complexType>
+      <xsd:simpleContent>
+        <xsd:extension base="xsd:string">
+          <xsd:attribute name="count-type" type="xlf:AttrType_count-type" use="optional"/>
+          <xsd:attribute name="phase-name" type="xsd:string" use="optional"/>
+          <xsd:attribute default="word" name="unit" type="xlf:AttrType_unit" use="optional"/>
+        </xsd:extension>
+      </xsd:simpleContent>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="context-group">
+    <xsd:complexType>
+      <xsd:sequence maxOccurs="unbounded">
+        <xsd:element ref="xlf:context"/>
+      </xsd:sequence>
+      <xsd:attribute name="name" type="xsd:string" use="optional"/>
+      <xsd:attribute name="crc" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="purpose" type="xlf:AttrType_purpose" use="optional"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="context">
+    <xsd:complexType>
+      <xsd:simpleContent>
+        <xsd:extension base="xsd:string">
+          <xsd:attribute name="context-type" type="xlf:AttrType_context-type" use="required"/>
+          <xsd:attribute default="no" name="match-mandatory" type="xlf:AttrType_YesNo" use="optional"/>
+          <xsd:attribute name="crc" type="xsd:NMTOKEN" use="optional"/>
+        </xsd:extension>
+      </xsd:simpleContent>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="tool">
+    <xsd:complexType mixed="true">
+      <xsd:sequence>
+        <xsd:any namespace="##any" processContents="strict" minOccurs="0" maxOccurs="unbounded"/>
+      </xsd:sequence>
+      <xsd:attribute name="tool-id" type="xsd:string" use="required"/>
+      <xsd:attribute name="tool-name" type="xsd:string" use="required"/>
+      <xsd:attribute name="tool-version" type="xsd:string" use="optional"/>
+      <xsd:attribute name="tool-company" type="xsd:string" use="optional"/>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="body">
+    <xsd:complexType>
+      <xsd:choice maxOccurs="unbounded" minOccurs="0">
+        <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:group"/>
+        <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:trans-unit"/>
+        <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:bin-unit"/>
+      </xsd:choice>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="group">
+    <xsd:complexType>
+      <xsd:sequence>
+        <xsd:sequence>
+          <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:context-group"/>
+          <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:count-group"/>
+          <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:note"/>
+          <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="strict"/>
+        </xsd:sequence>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:group"/>
+          <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:trans-unit"/>
+          <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:bin-unit"/>
+        </xsd:choice>
+      </xsd:sequence>
+      <xsd:attribute name="id" type="xsd:string" use="optional"/>
+      <xsd:attribute name="datatype" type="xlf:AttrType_datatype" use="optional"/>
+      <xsd:attribute default="default" ref="xml:space" use="optional"/>
+      <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/>
+      <xsd:attribute name="resname" type="xsd:string" use="optional"/>
+      <xsd:attribute name="extradata" type="xsd:string" use="optional"/>
+      <xsd:attribute name="extype" type="xsd:string" use="optional"/>
+      <xsd:attribute name="help-id" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="menu" type="xsd:string" use="optional"/>
+      <xsd:attribute name="menu-option" type="xsd:string" use="optional"/>
+      <xsd:attribute name="menu-name" type="xsd:string" use="optional"/>
+      <xsd:attribute name="coord" type="xlf:AttrType_Coordinates" use="optional"/>
+      <xsd:attribute name="font" type="xsd:string" use="optional"/>
+      <xsd:attribute name="css-style" type="xsd:string" use="optional"/>
+      <xsd:attribute name="style" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="exstyle" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute default="yes" name="translate" type="xlf:AttrType_YesNo" use="optional"/>
+      <xsd:attribute default="yes" name="reformat" type="xlf:AttrType_reformat" use="optional"/>
+      <xsd:attribute default="pixel" name="size-unit" type="xlf:AttrType_size-unit" use="optional"/>
+      <xsd:attribute name="maxwidth" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="minwidth" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="maxheight" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="minheight" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="maxbytes" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="minbytes" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="charclass" type="xsd:string" use="optional"/>
+      <xsd:attribute default="no" name="merged-trans" type="xlf:AttrType_YesNo" use="optional"/>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="trans-unit">
+    <xsd:complexType>
+      <xsd:sequence>
+        <xsd:element ref="xlf:source"/>
+        <xsd:element minOccurs="0" ref="xlf:seg-source"/>
+        <xsd:element minOccurs="0" ref="xlf:target"/>
+        <xsd:choice maxOccurs="unbounded" minOccurs="0">
+          <xsd:element ref="xlf:context-group"/>
+          <xsd:element ref="xlf:count-group"/>
+          <xsd:element ref="xlf:note"/>
+          <xsd:element ref="xlf:alt-trans"/>
+        </xsd:choice>
+        <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="strict"/>
+      </xsd:sequence>
+      <xsd:attribute name="id" type="xsd:string" use="required"/>
+      <xsd:attribute name="approved" type="xlf:AttrType_YesNo" use="optional"/>
+      <xsd:attribute default="yes" name="translate" type="xlf:AttrType_YesNo" use="optional"/>
+      <xsd:attribute default="yes" name="reformat" type="xlf:AttrType_reformat" use="optional"/>
+      <xsd:attribute default="default" ref="xml:space" use="optional"/>
+      <xsd:attribute name="datatype" type="xlf:AttrType_datatype" use="optional"/>
+      <xsd:attribute name="phase-name" type="xsd:string" use="optional"/>
+      <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/>
+      <xsd:attribute name="resname" type="xsd:string" use="optional"/>
+      <xsd:attribute name="extradata" type="xsd:string" use="optional"/>
+      <xsd:attribute name="extype" type="xsd:string" use="optional"/>
+      <xsd:attribute name="help-id" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="menu" type="xsd:string" use="optional"/>
+      <xsd:attribute name="menu-option" type="xsd:string" use="optional"/>
+      <xsd:attribute name="menu-name" type="xsd:string" use="optional"/>
+      <xsd:attribute name="coord" type="xlf:AttrType_Coordinates" use="optional"/>
+      <xsd:attribute name="font" type="xsd:string" use="optional"/>
+      <xsd:attribute name="css-style" type="xsd:string" use="optional"/>
+      <xsd:attribute name="style" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="exstyle" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute default="pixel" name="size-unit" type="xlf:AttrType_size-unit" use="optional"/>
+      <xsd:attribute name="maxwidth" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="minwidth" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="maxheight" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="minheight" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="maxbytes" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="minbytes" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="charclass" type="xsd:string" use="optional"/>
+      <xsd:attribute default="yes" name="merged-trans" type="xlf:AttrType_YesNo" use="optional"/>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+    <xsd:unique name="U_tu_segsrc_mid">
+      <xsd:selector xpath="./xlf:seg-source/xlf:mrk"/>
+      <xsd:field xpath="@mid"/>
+    </xsd:unique>
+    <xsd:keyref name="KR_tu_segsrc_mid" refer="xlf:U_tu_segsrc_mid">
+      <xsd:selector xpath="./xlf:target/xlf:mrk|./xlf:alt-trans"/>
+      <xsd:field xpath="@mid"/>
+    </xsd:keyref>
+  </xsd:element>
+  <xsd:element name="source">
+    <xsd:complexType mixed="true">
+      <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/>
+      <xsd:attribute ref="xml:lang" use="optional"/>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+    <xsd:unique name="U_source_bpt_rid">
+      <xsd:selector xpath=".//xlf:bpt"/>
+      <xsd:field xpath="@rid"/>
+    </xsd:unique>
+    <xsd:keyref name="KR_source_ept_rid" refer="xlf:U_source_bpt_rid">
+      <xsd:selector xpath=".//xlf:ept"/>
+      <xsd:field xpath="@rid"/>
+    </xsd:keyref>
+    <xsd:unique name="U_source_bx_rid">
+      <xsd:selector xpath=".//xlf:bx"/>
+      <xsd:field xpath="@rid"/>
+    </xsd:unique>
+    <xsd:keyref name="KR_source_ex_rid" refer="xlf:U_source_bx_rid">
+      <xsd:selector xpath=".//xlf:ex"/>
+      <xsd:field xpath="@rid"/>
+    </xsd:keyref>
+  </xsd:element>
+  <xsd:element name="seg-source">
+    <xsd:complexType mixed="true">
+      <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/>
+      <xsd:attribute ref="xml:lang" use="optional"/>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+    <xsd:unique name="U_segsrc_bpt_rid">
+      <xsd:selector xpath=".//xlf:bpt"/>
+      <xsd:field xpath="@rid"/>
+    </xsd:unique>
+    <xsd:keyref name="KR_segsrc_ept_rid" refer="xlf:U_segsrc_bpt_rid">
+      <xsd:selector xpath=".//xlf:ept"/>
+      <xsd:field xpath="@rid"/>
+    </xsd:keyref>
+    <xsd:unique name="U_segsrc_bx_rid">
+      <xsd:selector xpath=".//xlf:bx"/>
+      <xsd:field xpath="@rid"/>
+    </xsd:unique>
+    <xsd:keyref name="KR_segsrc_ex_rid" refer="xlf:U_segsrc_bx_rid">
+      <xsd:selector xpath=".//xlf:ex"/>
+      <xsd:field xpath="@rid"/>
+    </xsd:keyref>
+  </xsd:element>
+  <xsd:element name="target">
+    <xsd:complexType mixed="true">
+      <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/>
+      <xsd:attribute name="state" type="xlf:AttrType_state" use="optional"/>
+      <xsd:attribute name="state-qualifier" type="xlf:AttrType_state-qualifier" use="optional"/>
+      <xsd:attribute name="phase-name" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute ref="xml:lang" use="optional"/>
+      <xsd:attribute name="resname" type="xsd:string" use="optional"/>
+      <xsd:attribute name="coord" type="xlf:AttrType_Coordinates" use="optional"/>
+      <xsd:attribute name="font" type="xsd:string" use="optional"/>
+      <xsd:attribute name="css-style" type="xsd:string" use="optional"/>
+      <xsd:attribute name="style" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="exstyle" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute default="yes" name="equiv-trans" type="xlf:AttrType_YesNo" use="optional"/>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+    <xsd:unique name="U_target_bpt_rid">
+      <xsd:selector xpath=".//xlf:bpt"/>
+      <xsd:field xpath="@rid"/>
+    </xsd:unique>
+    <xsd:keyref name="KR_target_ept_rid" refer="xlf:U_target_bpt_rid">
+      <xsd:selector xpath=".//xlf:ept"/>
+      <xsd:field xpath="@rid"/>
+    </xsd:keyref>
+    <xsd:unique name="U_target_bx_rid">
+      <xsd:selector xpath=".//xlf:bx"/>
+      <xsd:field xpath="@rid"/>
+    </xsd:unique>
+    <xsd:keyref name="KR_target_ex_rid" refer="xlf:U_target_bx_rid">
+      <xsd:selector xpath=".//xlf:ex"/>
+      <xsd:field xpath="@rid"/>
+    </xsd:keyref>
+  </xsd:element>
+  <xsd:element name="alt-trans">
+    <xsd:complexType>
+      <xsd:sequence>
+        <xsd:element minOccurs="0" ref="xlf:source"/>
+        <xsd:element minOccurs="0" ref="xlf:seg-source"/>
+        <xsd:element maxOccurs="1" ref="xlf:target"/>
+        <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:context-group"/>
+        <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:note"/>
+        <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="strict"/>
+      </xsd:sequence>
+      <xsd:attribute name="match-quality" type="xsd:string" use="optional"/>
+      <xsd:attribute name="tool-id" type="xsd:string" use="optional"/>
+      <xsd:attribute name="crc" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute ref="xml:lang" use="optional"/>
+      <xsd:attribute name="origin" type="xsd:string" use="optional"/>
+      <xsd:attribute name="datatype" type="xlf:AttrType_datatype" use="optional"/>
+      <xsd:attribute default="default" ref="xml:space" use="optional"/>
+      <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/>
+      <xsd:attribute name="resname" type="xsd:string" use="optional"/>
+      <xsd:attribute name="extradata" type="xsd:string" use="optional"/>
+      <xsd:attribute name="extype" type="xsd:string" use="optional"/>
+      <xsd:attribute name="help-id" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="menu" type="xsd:string" use="optional"/>
+      <xsd:attribute name="menu-option" type="xsd:string" use="optional"/>
+      <xsd:attribute name="menu-name" type="xsd:string" use="optional"/>
+      <xsd:attribute name="mid" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="coord" type="xlf:AttrType_Coordinates" use="optional"/>
+      <xsd:attribute name="font" type="xsd:string" use="optional"/>
+      <xsd:attribute name="css-style" type="xsd:string" use="optional"/>
+      <xsd:attribute name="style" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="exstyle" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="phase-name" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute default="proposal" name="alttranstype" type="xlf:AttrType_alttranstype" use="optional"/>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+    <xsd:unique name="U_at_segsrc_mid">
+      <xsd:selector xpath="./xlf:seg-source/xlf:mrk"/>
+      <xsd:field xpath="@mid"/>
+    </xsd:unique>
+    <xsd:keyref name="KR_at_segsrc_mid" refer="xlf:U_at_segsrc_mid">
+      <xsd:selector xpath="./xlf:target/xlf:mrk"/>
+      <xsd:field xpath="@mid"/>
+    </xsd:keyref>
+  </xsd:element>
+  <xsd:element name="bin-unit">
+    <xsd:complexType>
+      <xsd:sequence>
+        <xsd:element ref="xlf:bin-source"/>
+        <xsd:element minOccurs="0" ref="xlf:bin-target"/>
+        <xsd:choice maxOccurs="unbounded" minOccurs="0">
+          <xsd:element ref="xlf:context-group"/>
+          <xsd:element ref="xlf:count-group"/>
+          <xsd:element ref="xlf:note"/>
+          <xsd:element ref="xlf:trans-unit"/>
+        </xsd:choice>
+        <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="strict"/>
+      </xsd:sequence>
+      <xsd:attribute name="id" type="xsd:string" use="required"/>
+      <xsd:attribute name="mime-type" type="xlf:mime-typeValueList" use="required"/>
+      <xsd:attribute name="approved" type="xlf:AttrType_YesNo" use="optional"/>
+      <xsd:attribute default="yes" name="translate" type="xlf:AttrType_YesNo" use="optional"/>
+      <xsd:attribute default="yes" name="reformat" type="xlf:AttrType_reformat" use="optional"/>
+      <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/>
+      <xsd:attribute name="resname" type="xsd:string" use="optional"/>
+      <xsd:attribute name="phase-name" type="xsd:string" use="optional"/>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="bin-source">
+    <xsd:complexType>
+      <xsd:choice>
+        <xsd:element ref="xlf:internal-file"/>
+        <xsd:element ref="xlf:external-file"/>
+      </xsd:choice>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="bin-target">
+    <xsd:complexType>
+      <xsd:choice>
+        <xsd:element ref="xlf:internal-file"/>
+        <xsd:element ref="xlf:external-file"/>
+      </xsd:choice>
+      <xsd:attribute name="mime-type" type="xlf:mime-typeValueList" use="optional"/>
+      <xsd:attribute name="state" type="xlf:AttrType_state" use="optional"/>
+      <xsd:attribute name="state-qualifier" type="xlf:AttrType_state-qualifier" use="optional"/>
+      <xsd:attribute name="phase-name" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/>
+      <xsd:attribute name="resname" type="xsd:string" use="optional"/>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+  </xsd:element>
+  <!-- Element for inline codes -->
+  <xsd:element name="g">
+    <xsd:complexType mixed="true">
+      <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/>
+      <xsd:attribute name="ctype" type="xlf:AttrType_InlineDelimiters" use="optional"/>
+      <xsd:attribute default="yes" name="clone" type="xlf:AttrType_YesNo" use="optional"/>
+      <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="x">
+    <xsd:complexType>
+      <xsd:attribute name="ctype" type="xlf:AttrType_InlinePlaceholders" use="optional"/>
+      <xsd:attribute default="yes" name="clone" type="xlf:AttrType_YesNo" use="optional"/>
+      <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="bx">
+    <xsd:complexType>
+      <xsd:attribute name="rid" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="ctype" type="xlf:AttrType_InlineDelimiters" use="optional"/>
+      <xsd:attribute default="yes" name="clone" type="xlf:AttrType_YesNo" use="optional"/>
+      <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="ex">
+    <xsd:complexType>
+      <xsd:attribute name="rid" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="ph">
+    <xsd:complexType mixed="true">
+      <xsd:sequence maxOccurs="unbounded" minOccurs="0">
+        <xsd:element ref="xlf:sub"/>
+      </xsd:sequence>
+      <xsd:attribute name="ctype" type="xlf:AttrType_InlinePlaceholders" use="optional"/>
+      <xsd:attribute name="crc" type="xsd:string" use="optional"/>
+      <xsd:attribute name="assoc" type="xlf:AttrType_assoc" use="optional"/>
+      <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="bpt">
+    <xsd:complexType mixed="true">
+      <xsd:sequence maxOccurs="unbounded" minOccurs="0">
+        <xsd:element ref="xlf:sub"/>
+      </xsd:sequence>
+      <xsd:attribute name="rid" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="ctype" type="xlf:AttrType_InlineDelimiters" use="optional"/>
+      <xsd:attribute name="crc" type="xsd:string" use="optional"/>
+      <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="ept">
+    <xsd:complexType mixed="true">
+      <xsd:sequence maxOccurs="unbounded" minOccurs="0">
+        <xsd:element ref="xlf:sub"/>
+      </xsd:sequence>
+      <xsd:attribute name="rid" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="crc" type="xsd:string" use="optional"/>
+      <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="it">
+    <xsd:complexType mixed="true">
+      <xsd:sequence maxOccurs="unbounded" minOccurs="0">
+        <xsd:element ref="xlf:sub"/>
+      </xsd:sequence>
+      <xsd:attribute name="pos" type="xlf:AttrType_Position" use="required"/>
+      <xsd:attribute name="rid" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="ctype" type="xlf:AttrType_InlineDelimiters" use="optional"/>
+      <xsd:attribute name="crc" type="xsd:string" use="optional"/>
+      <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="sub">
+    <xsd:complexType mixed="true">
+      <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/>
+      <xsd:attribute name="datatype" type="xlf:AttrType_datatype" use="optional"/>
+      <xsd:attribute name="ctype" type="xlf:AttrType_InlineDelimiters" use="optional"/>
+      <xsd:attribute name="xid" type="xsd:string" use="optional"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="mrk">
+    <xsd:complexType mixed="true">
+      <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/>
+      <xsd:attribute name="mtype" type="xlf:AttrType_mtype" use="required"/>
+      <xsd:attribute name="mid" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="comment" type="xsd:string" use="optional"/>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+  </xsd:element>
+</xsd:schema>
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Loader/schema/dic/xliff-core/xml.xsd b/vendor/symfony/translation/Symfony/Component/Translation/Loader/schema/dic/xliff-core/xml.xsd
new file mode 100644 (file)
index 0000000..8fcb991
--- /dev/null
@@ -0,0 +1,309 @@
+<?xml version='1.0'?>
+<?xml-stylesheet href="../2008/09/xsd.xsl" type="text/xsl"?>
+<xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace" 
+  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
+  xmlns   ="http://www.w3.org/1999/xhtml"
+  xml:lang="en">
+
+ <xs:annotation>
+  <xs:documentation>
+   <div>
+    <h1>About the XML namespace</h1>
+
+    <div class="bodytext">
+     <p>
+
+      This schema document describes the XML namespace, in a form
+      suitable for import by other schema documents.
+     </p>
+     <p>
+      See <a href="http://www.w3.org/XML/1998/namespace.html">
+      http://www.w3.org/XML/1998/namespace.html</a> and
+      <a href="http://www.w3.org/TR/REC-xml">
+      http://www.w3.org/TR/REC-xml</a> for information 
+      about this namespace.
+     </p>
+
+     <p>
+      Note that local names in this namespace are intended to be
+      defined only by the World Wide Web Consortium or its subgroups.
+      The names currently defined in this namespace are listed below.
+      They should not be used with conflicting semantics by any Working
+      Group, specification, or document instance.
+     </p>
+     <p>   
+      See further below in this document for more information about <a
+      href="#usage">how to refer to this schema document from your own
+      XSD schema documents</a> and about <a href="#nsversioning">the
+      namespace-versioning policy governing this schema document</a>.
+     </p>
+    </div>
+   </div>
+
+  </xs:documentation>
+ </xs:annotation>
+
+ <xs:attribute name="lang">
+  <xs:annotation>
+   <xs:documentation>
+    <div>
+     
+      <h3>lang (as an attribute name)</h3>
+      <p>
+
+       denotes an attribute whose value
+       is a language code for the natural language of the content of
+       any element; its value is inherited.  This name is reserved
+       by virtue of its definition in the XML specification.</p>
+     
+    </div>
+    <div>
+     <h4>Notes</h4>
+     <p>
+      Attempting to install the relevant ISO 2- and 3-letter
+      codes as the enumerated possible values is probably never
+      going to be a realistic possibility.  
+     </p>
+     <p>
+
+      See BCP 47 at <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt">
+       http://www.rfc-editor.org/rfc/bcp/bcp47.txt</a>
+      and the IANA language subtag registry at
+      <a href="http://www.iana.org/assignments/language-subtag-registry">
+       http://www.iana.org/assignments/language-subtag-registry</a>
+      for further information.
+     </p>
+     <p>
+
+      The union allows for the 'un-declaration' of xml:lang with
+      the empty string.
+     </p>
+    </div>
+   </xs:documentation>
+  </xs:annotation>
+  <xs:simpleType>
+   <xs:union memberTypes="xs:language">
+    <xs:simpleType>    
+     <xs:restriction base="xs:string">
+      <xs:enumeration value=""/>
+
+     </xs:restriction>
+    </xs:simpleType>
+   </xs:union>
+  </xs:simpleType>
+ </xs:attribute>
+
+ <xs:attribute name="space">
+  <xs:annotation>
+   <xs:documentation>
+
+    <div>
+     
+      <h3>space (as an attribute name)</h3>
+      <p>
+       denotes an attribute whose
+       value is a keyword indicating what whitespace processing
+       discipline is intended for the content of the element; its
+       value is inherited.  This name is reserved by virtue of its
+       definition in the XML specification.</p>
+     
+    </div>
+   </xs:documentation>
+  </xs:annotation>
+  <xs:simpleType>
+
+   <xs:restriction base="xs:NCName">
+    <xs:enumeration value="default"/>
+    <xs:enumeration value="preserve"/>
+   </xs:restriction>
+  </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="base" type="xs:anyURI"> <xs:annotation>
+   <xs:documentation>
+
+    <div>
+     
+      <h3>base (as an attribute name)</h3>
+      <p>
+       denotes an attribute whose value
+       provides a URI to be used as the base for interpreting any
+       relative URIs in the scope of the element on which it
+       appears; its value is inherited.  This name is reserved
+       by virtue of its definition in the XML Base specification.</p>
+     
+     <p>
+      See <a
+      href="http://www.w3.org/TR/xmlbase/">http://www.w3.org/TR/xmlbase/</a>
+      for information about this attribute.
+     </p>
+
+    </div>
+   </xs:documentation>
+  </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="id" type="xs:ID">
+  <xs:annotation>
+   <xs:documentation>
+    <div>
+     
+      <h3>id (as an attribute name)</h3> 
+      <p>
+
+       denotes an attribute whose value
+       should be interpreted as if declared to be of type ID.
+       This name is reserved by virtue of its definition in the
+       xml:id specification.</p>
+     
+     <p>
+      See <a
+      href="http://www.w3.org/TR/xml-id/">http://www.w3.org/TR/xml-id/</a>
+      for information about this attribute.
+     </p>
+    </div>
+   </xs:documentation>
+  </xs:annotation>
+
+ </xs:attribute>
+
+ <xs:attributeGroup name="specialAttrs">
+  <xs:attribute ref="xml:base"/>
+  <xs:attribute ref="xml:lang"/>
+  <xs:attribute ref="xml:space"/>
+  <xs:attribute ref="xml:id"/>
+ </xs:attributeGroup>
+
+ <xs:annotation>
+
+  <xs:documentation>
+   <div>
+   
+    <h3>Father (in any context at all)</h3> 
+
+    <div class="bodytext">
+     <p>
+      denotes Jon Bosak, the chair of 
+      the original XML Working Group.  This name is reserved by 
+      the following decision of the W3C XML Plenary and 
+      XML Coordination groups:
+     </p>
+     <blockquote>
+       <p>
+
+       In appreciation for his vision, leadership and
+       dedication the W3C XML Plenary on this 10th day of
+       February, 2000, reserves for Jon Bosak in perpetuity
+       the XML name "xml:Father".
+       </p>
+     </blockquote>
+    </div>
+   </div>
+  </xs:documentation>
+ </xs:annotation>
+
+ <xs:annotation>
+  <xs:documentation>
+
+   <div xml:id="usage" id="usage">
+    <h2><a name="usage">About this schema document</a></h2>
+
+    <div class="bodytext">
+     <p>
+      This schema defines attributes and an attribute group suitable
+      for use by schemas wishing to allow <code>xml:base</code>,
+      <code>xml:lang</code>, <code>xml:space</code> or
+      <code>xml:id</code> attributes on elements they define.
+     </p>
+
+     <p>
+      To enable this, such a schema must import this schema for
+      the XML namespace, e.g. as follows:
+     </p>
+     <pre>
+          &lt;schema.. .>
+          .. .
+           &lt;import namespace="http://www.w3.org/XML/1998/namespace"
+                      schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+     </pre>
+     <p>
+      or
+     </p>
+     <pre>
+
+           &lt;import namespace="http://www.w3.org/XML/1998/namespace"
+                      schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
+     </pre>
+     <p>
+      Subsequently, qualified reference to any of the attributes or the
+      group defined below will have the desired effect, e.g.
+     </p>
+     <pre>
+          &lt;type.. .>
+          .. .
+           &lt;attributeGroup ref="xml:specialAttrs"/>
+     </pre>
+     <p>
+      will define a type which will schema-validate an instance element
+      with any of those attributes.
+     </p>
+
+    </div>
+   </div>
+  </xs:documentation>
+ </xs:annotation>
+
+ <xs:annotation>
+  <xs:documentation>
+   <div id="nsversioning" xml:id="nsversioning">
+    <h2><a name="nsversioning">Versioning policy for this schema document</a></h2>
+
+    <div class="bodytext">
+     <p>
+      In keeping with the XML Schema WG's standard versioning
+      policy, this schema document will persist at
+      <a href="http://www.w3.org/2009/01/xml.xsd">
+       http://www.w3.org/2009/01/xml.xsd</a>.
+     </p>
+     <p>
+      At the date of issue it can also be found at
+      <a href="http://www.w3.org/2001/xml.xsd">
+       http://www.w3.org/2001/xml.xsd</a>.
+     </p>
+
+     <p>
+      The schema document at that URI may however change in the future,
+      in order to remain compatible with the latest version of XML
+      Schema itself, or with the XML namespace itself.  In other words,
+      if the XML Schema or XML namespaces change, the version of this
+      document at <a href="http://www.w3.org/2001/xml.xsd">
+       http://www.w3.org/2001/xml.xsd 
+      </a> 
+      will change accordingly; the version at 
+      <a href="http://www.w3.org/2009/01/xml.xsd">
+       http://www.w3.org/2009/01/xml.xsd 
+      </a> 
+      will not change.
+     </p>
+     <p>
+
+      Previous dated (and unchanging) versions of this schema 
+      document are at:
+     </p>
+     <ul>
+      <li><a href="http://www.w3.org/2009/01/xml.xsd">
+       http://www.w3.org/2009/01/xml.xsd</a></li>
+      <li><a href="http://www.w3.org/2007/08/xml.xsd">
+       http://www.w3.org/2007/08/xml.xsd</a></li>
+      <li><a href="http://www.w3.org/2004/10/xml.xsd">
+
+       http://www.w3.org/2004/10/xml.xsd</a></li>
+      <li><a href="http://www.w3.org/2001/03/xml.xsd">
+       http://www.w3.org/2001/03/xml.xsd</a></li>
+     </ul>
+    </div>
+   </div>
+  </xs:documentation>
+ </xs:annotation>
+
+</xs:schema>
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/MessageCatalogue.php b/vendor/symfony/translation/Symfony/Component/Translation/MessageCatalogue.php
new file mode 100644 (file)
index 0000000..1d8a08d
--- /dev/null
@@ -0,0 +1,295 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+use Symfony\Component\Config\Resource\ResourceInterface;
+
+/**
+ * MessageCatalogue.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterface
+{
+    private $messages = array();
+    private $metadata = array();
+    private $resources = array();
+    private $locale;
+    private $fallbackCatalogue;
+    private $parent;
+
+    /**
+     * Constructor.
+     *
+     * @param string $locale   The locale
+     * @param array  $messages An array of messages classified by domain
+     *
+     * @api
+     */
+    public function __construct($locale, array $messages = array())
+    {
+        $this->locale = $locale;
+        $this->messages = $messages;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function getLocale()
+    {
+        return $this->locale;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function getDomains()
+    {
+        return array_keys($this->messages);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function all($domain = null)
+    {
+        if (null === $domain) {
+            return $this->messages;
+        }
+
+        return isset($this->messages[$domain]) ? $this->messages[$domain] : array();
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function set($id, $translation, $domain = 'messages')
+    {
+        $this->add(array($id => $translation), $domain);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function has($id, $domain = 'messages')
+    {
+        if (isset($this->messages[$domain][$id])) {
+            return true;
+        }
+
+        if (null !== $this->fallbackCatalogue) {
+            return $this->fallbackCatalogue->has($id, $domain);
+        }
+
+        return false;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function defines($id, $domain = 'messages')
+    {
+        return isset($this->messages[$domain][$id]);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function get($id, $domain = 'messages')
+    {
+        if (isset($this->messages[$domain][$id])) {
+            return $this->messages[$domain][$id];
+        }
+
+        if (null !== $this->fallbackCatalogue) {
+            return $this->fallbackCatalogue->get($id, $domain);
+        }
+
+        return $id;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function replace($messages, $domain = 'messages')
+    {
+        $this->messages[$domain] = array();
+
+        $this->add($messages, $domain);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function add($messages, $domain = 'messages')
+    {
+        if (!isset($this->messages[$domain])) {
+            $this->messages[$domain] = $messages;
+        } else {
+            $this->messages[$domain] = array_replace($this->messages[$domain], $messages);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function addCatalogue(MessageCatalogueInterface $catalogue)
+    {
+        if ($catalogue->getLocale() !== $this->locale) {
+            throw new \LogicException(sprintf('Cannot add a catalogue for locale "%s" as the current locale for this catalogue is "%s"', $catalogue->getLocale(), $this->locale));
+        }
+
+        foreach ($catalogue->all() as $domain => $messages) {
+            $this->add($messages, $domain);
+        }
+
+        foreach ($catalogue->getResources() as $resource) {
+            $this->addResource($resource);
+        }
+
+        if ($catalogue instanceof MetadataAwareInterface) {
+            $metadata = $catalogue->getMetadata('', '');
+            $this->addMetadata($metadata);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function addFallbackCatalogue(MessageCatalogueInterface $catalogue)
+    {
+        // detect circular references
+        $c = $this;
+        do {
+            if ($c->getLocale() === $catalogue->getLocale()) {
+                throw new \LogicException(sprintf('Circular reference detected when adding a fallback catalogue for locale "%s".', $catalogue->getLocale()));
+            }
+        } while ($c = $c->parent);
+
+        $catalogue->parent = $this;
+        $this->fallbackCatalogue = $catalogue;
+
+        foreach ($catalogue->getResources() as $resource) {
+            $this->addResource($resource);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function getFallbackCatalogue()
+    {
+        return $this->fallbackCatalogue;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function getResources()
+    {
+        return array_values($this->resources);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function addResource(ResourceInterface $resource)
+    {
+        $this->resources[$resource->__toString()] = $resource;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getMetadata($key = '', $domain = 'messages')
+    {
+        if ('' == $domain) {
+            return $this->metadata;
+        }
+
+        if (isset($this->metadata[$domain])) {
+            if ('' == $key) {
+                return $this->metadata[$domain];
+            }
+
+            if (isset($this->metadata[$domain][$key])) {
+                return $this->metadata[$domain][$key];
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setMetadata($key, $value, $domain = 'messages')
+    {
+        $this->metadata[$domain][$key] = $value;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function deleteMetadata($key = '', $domain = 'messages')
+    {
+        if ('' == $domain) {
+            $this->metadata = array();
+        } elseif ('' == $key) {
+            unset($this->metadata[$domain]);
+        } else {
+            unset($this->metadata[$domain][$key]);
+        }
+    }
+
+    /**
+     * Adds current values with the new values.
+     *
+     * @param array $values Values to add
+     */
+    private function addMetadata(array $values)
+    {
+        foreach ($values as $domain => $keys) {
+            foreach ($keys as $key => $value) {
+                $this->setMetadata($key, $value, $domain);
+            }
+        }
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/MessageCatalogueInterface.php b/vendor/symfony/translation/Symfony/Component/Translation/MessageCatalogueInterface.php
new file mode 100644 (file)
index 0000000..5e9be20
--- /dev/null
@@ -0,0 +1,172 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+use Symfony\Component\Config\Resource\ResourceInterface;
+
+/**
+ * MessageCatalogueInterface.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+interface MessageCatalogueInterface
+{
+    /**
+     * Gets the catalogue locale.
+     *
+     * @return string The locale
+     *
+     * @api
+     */
+    public function getLocale();
+
+    /**
+     * Gets the domains.
+     *
+     * @return array An array of domains
+     *
+     * @api
+     */
+    public function getDomains();
+
+    /**
+     * Gets the messages within a given domain.
+     *
+     * If $domain is null, it returns all messages.
+     *
+     * @param string $domain The domain name
+     *
+     * @return array An array of messages
+     *
+     * @api
+     */
+    public function all($domain = null);
+
+    /**
+     * Sets a message translation.
+     *
+     * @param string $id          The message id
+     * @param string $translation The messages translation
+     * @param string $domain      The domain name
+     *
+     * @api
+     */
+    public function set($id, $translation, $domain = 'messages');
+
+    /**
+     * Checks if a message has a translation.
+     *
+     * @param string $id     The message id
+     * @param string $domain The domain name
+     *
+     * @return Boolean true if the message has a translation, false otherwise
+     *
+     * @api
+     */
+    public function has($id, $domain = 'messages');
+
+    /**
+     * Checks if a message has a translation (it does not take into account the fallback mechanism).
+     *
+     * @param string $id     The message id
+     * @param string $domain The domain name
+     *
+     * @return Boolean true if the message has a translation, false otherwise
+     *
+     * @api
+     */
+    public function defines($id, $domain = 'messages');
+
+    /**
+     * Gets a message translation.
+     *
+     * @param string $id     The message id
+     * @param string $domain The domain name
+     *
+     * @return string The message translation
+     *
+     * @api
+     */
+    public function get($id, $domain = 'messages');
+
+    /**
+     * Sets translations for a given domain.
+     *
+     * @param array  $messages An array of translations
+     * @param string $domain   The domain name
+     *
+     * @api
+     */
+    public function replace($messages, $domain = 'messages');
+
+    /**
+     * Adds translations for a given domain.
+     *
+     * @param array  $messages An array of translations
+     * @param string $domain   The domain name
+     *
+     * @api
+     */
+    public function add($messages, $domain = 'messages');
+
+    /**
+     * Merges translations from the given Catalogue into the current one.
+     *
+     * The two catalogues must have the same locale.
+     *
+     * @param MessageCatalogueInterface $catalogue A MessageCatalogueInterface instance
+     *
+     * @api
+     */
+    public function addCatalogue(MessageCatalogueInterface $catalogue);
+
+    /**
+     * Merges translations from the given Catalogue into the current one
+     * only when the translation does not exist.
+     *
+     * This is used to provide default translations when they do not exist for the current locale.
+     *
+     * @param MessageCatalogueInterface $catalogue A MessageCatalogueInterface instance
+     *
+     * @api
+     */
+    public function addFallbackCatalogue(MessageCatalogueInterface $catalogue);
+
+    /**
+     * Gets the fallback catalogue.
+     *
+     * @return MessageCatalogueInterface|null A MessageCatalogueInterface instance or null when no fallback has been set
+     *
+     * @api
+     */
+    public function getFallbackCatalogue();
+
+    /**
+     * Returns an array of resources loaded to build this collection.
+     *
+     * @return ResourceInterface[] An array of resources
+     *
+     * @api
+     */
+    public function getResources();
+
+    /**
+     * Adds a resource for this collection.
+     *
+     * @param ResourceInterface $resource A resource instance
+     *
+     * @api
+     */
+    public function addResource(ResourceInterface $resource);
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/MessageSelector.php b/vendor/symfony/translation/Symfony/Component/Translation/MessageSelector.php
new file mode 100644 (file)
index 0000000..387c964
--- /dev/null
@@ -0,0 +1,82 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+/**
+ * MessageSelector.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class MessageSelector
+{
+    /**
+     * Given a message with different plural translations separated by a
+     * pipe (|), this method returns the correct portion of the message based
+     * on the given number, locale and the pluralization rules in the message
+     * itself.
+     *
+     * The message supports two different types of pluralization rules:
+     *
+     * interval: {0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples
+     * indexed:  There is one apple|There are %count% apples
+     *
+     * The indexed solution can also contain labels (e.g. one: There is one apple).
+     * This is purely for making the translations more clear - it does not
+     * affect the functionality.
+     *
+     * The two methods can also be mixed:
+     *     {0} There are no apples|one: There is one apple|more: There are %count% apples
+     *
+     * @param string  $message The message being translated
+     * @param integer $number  The number of items represented for the message
+     * @param string  $locale  The locale to use for choosing
+     *
+     * @return string
+     *
+     * @throws \InvalidArgumentException
+     *
+     * @api
+     */
+    public function choose($message, $number, $locale)
+    {
+        $parts = explode('|', $message);
+        $explicitRules = array();
+        $standardRules = array();
+        foreach ($parts as $part) {
+            $part = trim($part);
+
+            if (preg_match('/^(?P<interval>'.Interval::getIntervalRegexp().')\s*(?P<message>.*?)$/x', $part, $matches)) {
+                $explicitRules[$matches['interval']] = $matches['message'];
+            } elseif (preg_match('/^\w+\:\s*(.*?)$/', $part, $matches)) {
+                $standardRules[] = $matches[1];
+            } else {
+                $standardRules[] = $part;
+            }
+        }
+
+        // try to match an explicit rule, then fallback to the standard ones
+        foreach ($explicitRules as $interval => $m) {
+            if (Interval::test($number, $interval)) {
+                return $m;
+            }
+        }
+
+        $position = PluralizationRules::get($number, $locale);
+        if (!isset($standardRules[$position])) {
+            throw new \InvalidArgumentException(sprintf('Unable to choose a translation for "%s" with locale "%s". Double check that this translation has the correct plural options (e.g. "There is one apple|There are %%count%% apples").', $message, $locale));
+        }
+
+        return $standardRules[$position];
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/MetadataAwareInterface.php b/vendor/symfony/translation/Symfony/Component/Translation/MetadataAwareInterface.php
new file mode 100644 (file)
index 0000000..1c43935
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+/**
+ * MetadataAwareInterface.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface MetadataAwareInterface
+{
+    /**
+     * Gets metadata for the given domain and key.
+     *
+     * Passing an empty domain will return an array with all metadata indexed by
+     * domain and then by key. Passing an empty key will return an array with all
+     * metadata for the given domain.
+     *
+     * @param string $domain The domain name
+     * @param string $key    The key
+     *
+     * @return mixed The value that was set or an array with the domains/keys or null
+     */
+    public function getMetadata($key = '', $domain = 'messages');
+
+    /**
+     * Adds metadata to a message domain.
+     *
+     * @param string $key    The key
+     * @param mixed  $value  The value
+     * @param string $domain The domain name
+     */
+    public function setMetadata($key, $value, $domain = 'messages');
+
+    /**
+     * Deletes metadata for the given key and domain.
+     *
+     * Passing an empty domain will delete all metadata. Passing an empty key will
+     * delete all metadata for the given domain.
+     *
+     * @param string $domain The domain name
+     * @param string $key    The key
+     */
+    public function deleteMetadata($key = '', $domain = 'messages');
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/PluralizationRules.php b/vendor/symfony/translation/Symfony/Component/Translation/PluralizationRules.php
new file mode 100644 (file)
index 0000000..5c63b8d
--- /dev/null
@@ -0,0 +1,219 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+/**
+ * Returns the plural rules for a given locale.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class PluralizationRules
+{
+    // @codeCoverageIgnoreStart
+    private static $rules = array();
+
+    /**
+     * Returns the plural position to use for the given locale and number.
+     *
+     * @param integer $number The number
+     * @param string  $locale The locale
+     *
+     * @return integer The plural position
+     */
+    public static function get($number, $locale)
+    {
+        if ("pt_BR" == $locale) {
+            // temporary set a locale for brazilian
+            $locale = "xbr";
+        }
+
+        if (strlen($locale) > 3) {
+            $locale = substr($locale, 0, -strlen(strrchr($locale, '_')));
+        }
+
+        if (isset(self::$rules[$locale])) {
+            $return = call_user_func(self::$rules[$locale], $number);
+
+            if (!is_int($return) || $return < 0) {
+                return 0;
+            }
+
+            return $return;
+        }
+
+        /*
+         * The plural rules are derived from code of the Zend Framework (2010-09-25),
+         * which is subject to the new BSD license (http://framework.zend.com/license/new-bsd).
+         * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+         */
+        switch ($locale) {
+            case 'bo':
+            case 'dz':
+            case 'id':
+            case 'ja':
+            case 'jv':
+            case 'ka':
+            case 'km':
+            case 'kn':
+            case 'ko':
+            case 'ms':
+            case 'th':
+            case 'tr':
+            case 'vi':
+            case 'zh':
+                return 0;
+                break;
+
+            case 'af':
+            case 'az':
+            case 'bn':
+            case 'bg':
+            case 'ca':
+            case 'da':
+            case 'de':
+            case 'el':
+            case 'en':
+            case 'eo':
+            case 'es':
+            case 'et':
+            case 'eu':
+            case 'fa':
+            case 'fi':
+            case 'fo':
+            case 'fur':
+            case 'fy':
+            case 'gl':
+            case 'gu':
+            case 'ha':
+            case 'he':
+            case 'hu':
+            case 'is':
+            case 'it':
+            case 'ku':
+            case 'lb':
+            case 'ml':
+            case 'mn':
+            case 'mr':
+            case 'nah':
+            case 'nb':
+            case 'ne':
+            case 'nl':
+            case 'nn':
+            case 'no':
+            case 'om':
+            case 'or':
+            case 'pa':
+            case 'pap':
+            case 'ps':
+            case 'pt':
+            case 'so':
+            case 'sq':
+            case 'sv':
+            case 'sw':
+            case 'ta':
+            case 'te':
+            case 'tk':
+            case 'ur':
+            case 'zu':
+                return ($number == 1) ? 0 : 1;
+
+            case 'am':
+            case 'bh':
+            case 'fil':
+            case 'fr':
+            case 'gun':
+            case 'hi':
+            case 'ln':
+            case 'mg':
+            case 'nso':
+            case 'xbr':
+            case 'ti':
+            case 'wa':
+                return (($number == 0) || ($number == 1)) ? 0 : 1;
+
+            case 'be':
+            case 'bs':
+            case 'hr':
+            case 'ru':
+            case 'sr':
+            case 'uk':
+                return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
+
+            case 'cs':
+            case 'sk':
+                return ($number == 1) ? 0 : ((($number >= 2) && ($number <= 4)) ? 1 : 2);
+
+            case 'ga':
+                return ($number == 1) ? 0 : (($number == 2) ? 1 : 2);
+
+            case 'lt':
+                return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
+
+            case 'sl':
+                return ($number % 100 == 1) ? 0 : (($number % 100 == 2) ? 1 : ((($number % 100 == 3) || ($number % 100 == 4)) ? 2 : 3));
+
+            case 'mk':
+                return ($number % 10 == 1) ? 0 : 1;
+
+            case 'mt':
+                return ($number == 1) ? 0 : ((($number == 0) || (($number % 100 > 1) && ($number % 100 < 11))) ? 1 : ((($number % 100 > 10) && ($number % 100 < 20)) ? 2 : 3));
+
+            case 'lv':
+                return ($number == 0) ? 0 : ((($number % 10 == 1) && ($number % 100 != 11)) ? 1 : 2);
+
+            case 'pl':
+                return ($number == 1) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 12) || ($number % 100 > 14))) ? 1 : 2);
+
+            case 'cy':
+                return ($number == 1) ? 0 : (($number == 2) ? 1 : ((($number == 8) || ($number == 11)) ? 2 : 3));
+
+            case 'ro':
+                return ($number == 1) ? 0 : ((($number == 0) || (($number % 100 > 0) && ($number % 100 < 20))) ? 1 : 2);
+
+            case 'ar':
+                return ($number == 0) ? 0 : (($number == 1) ? 1 : (($number == 2) ? 2 : ((($number >= 3) && ($number <= 10)) ? 3 : ((($number >= 11) && ($number <= 99)) ? 4 : 5))));
+
+            default:
+                return 0;
+        }
+    }
+
+    /**
+     * Overrides the default plural rule for a given locale.
+     *
+     * @param string $rule   A PHP callable
+     * @param string $locale The locale
+     *
+     * @return null
+     *
+     * @throws \LogicException
+     */
+    public static function set($rule, $locale)
+    {
+        if ("pt_BR" == $locale) {
+            // temporary set a locale for brazilian
+            $locale = "xbr";
+        }
+
+        if (strlen($locale) > 3) {
+            $locale = substr($locale, 0, -strlen(strrchr($locale, '_')));
+        }
+
+        if (!is_callable($rule)) {
+            throw new \LogicException('The given rule can not be called');
+        }
+
+        self::$rules[$locale] = $rule;
+    }
+
+    // @codeCoverageIgnoreEnd
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/README.md b/vendor/symfony/translation/Symfony/Component/Translation/README.md
new file mode 100644 (file)
index 0000000..24e6210
--- /dev/null
@@ -0,0 +1,35 @@
+Translation Component
+=====================
+
+Translation provides tools for loading translation files and generating
+translated strings from these including support for pluralization.
+
+    use Symfony\Component\Translation\Translator;
+    use Symfony\Component\Translation\MessageSelector;
+    use Symfony\Component\Translation\Loader\ArrayLoader;
+
+    $translator = new Translator('fr_FR', new MessageSelector());
+    $translator->setFallbackLocales(array('fr'));
+    $translator->addLoader('array', new ArrayLoader());
+    $translator->addResource('array', array(
+        'Hello World!' => 'Bonjour',
+    ), 'fr');
+
+    echo $translator->trans('Hello World!')."\n";
+
+Resources
+---------
+
+Silex integration:
+
+https://github.com/fabpot/Silex/blob/master/src/Silex/Provider/TranslationServiceProvider.php
+
+Documentation:
+
+http://symfony.com/doc/2.3/book/translation.html
+
+You can run the unit tests with the following command:
+
+    $ cd path/to/Symfony/Component/Translation/
+    $ composer.phar install --dev
+    $ phpunit
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/Catalogue/AbstractOperationTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/Catalogue/AbstractOperationTest.php
new file mode 100644 (file)
index 0000000..78023e1
--- /dev/null
@@ -0,0 +1,74 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Test\Catalogue;
+
+use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\MessageCatalogueInterface;
+
+abstract class AbstractOperationTest extends TestCase
+{
+    public function testGetEmptyDomains()
+    {
+        $this->assertEquals(
+            array(),
+            $this->createOperation(
+                new MessageCatalogue('en'),
+                new MessageCatalogue('en')
+            )->getDomains()
+        );
+    }
+
+    public function testGetMergedDomains()
+    {
+        $this->assertEquals(
+            array('a', 'b', 'c'),
+            $this->createOperation(
+                new MessageCatalogue('en', array('a' => array(), 'b' => array())),
+                new MessageCatalogue('en', array('b' => array(), 'c' => array()))
+            )->getDomains()
+        );
+    }
+
+    public function testGetMessagesFromUnknownDomain()
+    {
+        $this->setExpectedException('InvalidArgumentException');
+        $this->createOperation(
+            new MessageCatalogue('en'),
+            new MessageCatalogue('en')
+        )->getMessages('domain');
+    }
+
+    public function testGetEmptyMessages()
+    {
+        $this->assertEquals(
+            array(),
+            $this->createOperation(
+                new MessageCatalogue('en', array('a' => array())),
+                new MessageCatalogue('en')
+            )->getMessages('a')
+        );
+    }
+
+    public function testGetEmptyResult()
+    {
+        $this->assertEquals(
+            new MessageCatalogue('en'),
+            $this->createOperation(
+                new MessageCatalogue('en'),
+                new MessageCatalogue('en')
+            )->getResult()
+        );
+    }
+
+    abstract protected function createOperation(MessageCatalogueInterface $source, MessageCatalogueInterface $target);
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/Catalogue/DiffOperationTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/Catalogue/DiffOperationTest.php
new file mode 100644 (file)
index 0000000..b2e852d
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Test\Catalogue;
+
+use Symfony\Component\Translation\Catalogue\DiffOperation;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\MessageCatalogueInterface;
+
+class DiffOperationTest extends AbstractOperationTest
+{
+    public function testGetMessagesFromSingleDomain()
+    {
+        $operation = $this->createOperation(
+            new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b'))),
+            new MessageCatalogue('en', array('messages' => array('a' => 'new_a', 'c' => 'new_c')))
+        );
+
+        $this->assertEquals(
+            array('a' => 'old_a', 'c' => 'new_c'),
+            $operation->getMessages('messages')
+        );
+
+        $this->assertEquals(
+            array('c' => 'new_c'),
+            $operation->getNewMessages('messages')
+        );
+
+        $this->assertEquals(
+            array('b' => 'old_b'),
+            $operation->getObsoleteMessages('messages')
+        );
+    }
+
+    public function testGetResultFromSingleDomain()
+    {
+        $this->assertEquals(
+            new MessageCatalogue('en', array(
+                'messages' => array('a' => 'old_a', 'c' => 'new_c')
+            )),
+            $this->createOperation(
+                new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b'))),
+                new MessageCatalogue('en', array('messages' => array('a' => 'new_a', 'c' => 'new_c')))
+            )->getResult()
+        );
+    }
+
+    protected function createOperation(MessageCatalogueInterface $source, MessageCatalogueInterface $target)
+    {
+        return new DiffOperation($source, $target);
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/Catalogue/MergeOperationTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/Catalogue/MergeOperationTest.php
new file mode 100644 (file)
index 0000000..fa5118a
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Test\Catalogue;
+
+use Symfony\Component\Translation\Catalogue\MergeOperation;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\MessageCatalogueInterface;
+
+class MergeOperationTest extends AbstractOperationTest
+{
+    public function testGetMessagesFromSingleDomain()
+    {
+        $operation = $this->createOperation(
+            new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b'))),
+            new MessageCatalogue('en', array('messages' => array('a' => 'new_a', 'c' => 'new_c')))
+        );
+
+        $this->assertEquals(
+            array('a' => 'old_a', 'b' => 'old_b', 'c' => 'new_c'),
+            $operation->getMessages('messages')
+        );
+
+        $this->assertEquals(
+            array('c' => 'new_c'),
+            $operation->getNewMessages('messages')
+        );
+
+        $this->assertEquals(
+            array(),
+            $operation->getObsoleteMessages('messages')
+        );
+    }
+
+    public function testGetResultFromSingleDomain()
+    {
+        $this->assertEquals(
+            new MessageCatalogue('en', array(
+                'messages' => array('a' => 'old_a', 'b' => 'old_b', 'c' => 'new_c')
+            )),
+            $this->createOperation(
+                new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b'))),
+                new MessageCatalogue('en', array('messages' => array('a' => 'new_a', 'c' => 'new_c')))
+            )->getResult()
+        );
+    }
+
+    protected function createOperation(MessageCatalogueInterface $source, MessageCatalogueInterface $target)
+    {
+        return new MergeOperation($source, $target);
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/CsvFileDumperTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/CsvFileDumperTest.php
new file mode 100644 (file)
index 0000000..29177ff
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Dumper\CsvFileDumper;
+
+class CsvFileDumperTest extends \PHPUnit_Framework_TestCase
+{
+    public function testDump()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->add(array('foo' => 'bar', 'bar' => 'foo
+foo', 'foo;foo' => 'bar'));
+
+        $tempDir = sys_get_temp_dir();
+        $dumper = new CsvFileDumper();
+        $dumper->dump($catalogue, array('path' => $tempDir));
+
+        $this->assertEquals(file_get_contents(__DIR__.'/../fixtures/valid.csv'), file_get_contents($tempDir.'/messages.en.csv'));
+
+        unlink($tempDir.'/messages.en.csv');
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/IcuResFileDumperTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/IcuResFileDumperTest.php
new file mode 100644 (file)
index 0000000..d187ef1
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Dumper\IcuResFileDumper;
+
+class IcuResFileDumperTest extends \PHPUnit_Framework_TestCase
+{
+    public function testDump()
+    {
+        if (!extension_loaded('mbstring')) {
+            $this->markTestSkipped('This test requires mbstring to work.');
+        }
+
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->add(array('foo' => 'bar'));
+
+        $tempDir = sys_get_temp_dir();
+        $dumper = new IcuResFileDumper();
+        $dumper->dump($catalogue, array('path' => $tempDir));
+
+        $this->assertEquals(file_get_contents(__DIR__.'/../fixtures/resourcebundle/res/en.res'), file_get_contents($tempDir.'/messages/en.res'));
+
+        unlink($tempDir.'/messages/en.res');
+        rmdir($tempDir.'/messages');
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/IniFileDumperTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/IniFileDumperTest.php
new file mode 100644 (file)
index 0000000..2a2cefd
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Dumper\IniFileDumper;
+
+class IniFileDumperTest extends \PHPUnit_Framework_TestCase
+{
+    public function testDump()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->add(array('foo' => 'bar'));
+
+        $tempDir = sys_get_temp_dir();
+        $dumper = new IniFileDumper();
+        $dumper->dump($catalogue, array('path' => $tempDir));
+
+        $this->assertEquals(file_get_contents(__DIR__.'/../fixtures/resources.ini'), file_get_contents($tempDir.'/messages.en.ini'));
+
+        unlink($tempDir.'/messages.en.ini');
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/MoFileDumperTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/MoFileDumperTest.php
new file mode 100644 (file)
index 0000000..439a25c
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Dumper\MoFileDumper;
+
+class MoFileDumperTest extends \PHPUnit_Framework_TestCase
+{
+    public function testDump()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->add(array('foo' => 'bar'));
+
+        $tempDir = sys_get_temp_dir();
+        $dumper = new MoFileDumper();
+        $dumper->dump($catalogue, array('path' => $tempDir));
+        $this->assertEquals(file_get_contents(__DIR__.'/../fixtures/resources.mo'), file_get_contents($tempDir.'/messages.en.mo'));
+
+        unlink($tempDir.'/messages.en.mo');
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/PhpFileDumperTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/PhpFileDumperTest.php
new file mode 100644 (file)
index 0000000..18be5a0
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Dumper\PhpFileDumper;
+
+class PhpFileDumperTest extends \PHPUnit_Framework_TestCase
+{
+    public function testDump()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->add(array('foo' => 'bar'));
+
+        $tempDir = sys_get_temp_dir();
+        $dumper = new PhpFileDumper();
+        $dumper->dump($catalogue, array('path' => $tempDir));
+
+        $this->assertEquals(file_get_contents(__DIR__.'/../fixtures/resources.php'), file_get_contents($tempDir.'/messages.en.php'));
+
+        unlink($tempDir.'/messages.en.php');
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/PoFileDumperTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/PoFileDumperTest.php
new file mode 100644 (file)
index 0000000..0296d6b
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Dumper\PoFileDumper;
+
+class PoFileDumperTest extends \PHPUnit_Framework_TestCase
+{
+    public function testDump()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->add(array('foo' => 'bar'));
+
+        $tempDir = sys_get_temp_dir();
+        $dumper = new PoFileDumper();
+        $dumper->dump($catalogue, array('path' => $tempDir));
+        $this->assertEquals(file_get_contents(__DIR__.'/../fixtures/resources.po'), file_get_contents($tempDir.'/messages.en.po'));
+
+        unlink($tempDir.'/messages.en.po');
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/QtFileDumperTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/QtFileDumperTest.php
new file mode 100644 (file)
index 0000000..d7d8fb7
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Dumper\QtFileDumper;
+
+class QtFileDumperTest extends \PHPUnit_Framework_TestCase
+{
+    public function testDump()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->add(array('foo' => 'bar'), 'resources');
+
+        $tempDir = sys_get_temp_dir();
+        $dumper = new QtFileDumper();
+        $dumper->dump($catalogue, array('path' => $tempDir));
+
+        $this->assertEquals(file_get_contents(__DIR__.'/../fixtures/resources.ts'), file_get_contents($tempDir.'/resources.en.ts'));
+
+        unlink($tempDir.'/resources.en.ts');
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/XliffFileDumperTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/XliffFileDumperTest.php
new file mode 100644 (file)
index 0000000..bef3135
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Dumper\XliffFileDumper;
+
+class XliffFileDumperTest extends \PHPUnit_Framework_TestCase
+{
+    public function testDump()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->add(array('foo' => 'bar', 'key' => ''));
+
+        $tempDir = sys_get_temp_dir();
+        $dumper = new XliffFileDumper();
+        $dumper->dump($catalogue, array('path' => $tempDir));
+
+        $this->assertEquals(file_get_contents(__DIR__.'/../fixtures/resources-clean.xlf'), file_get_contents($tempDir.'/messages.en.xlf'));
+
+        unlink($tempDir.'/messages.en.xlf');
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/YamlFileDumperTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/YamlFileDumperTest.php
new file mode 100644 (file)
index 0000000..e4e68e0
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Dumper\YamlFileDumper;
+
+class YamlFileDumperTest extends \PHPUnit_Framework_TestCase
+{
+    protected function setUp()
+    {
+        if (!class_exists('Symfony\Component\Yaml\Yaml')) {
+            $this->markTestSkipped('The "Yaml" component is not available');
+        }
+    }
+
+    public function testDump()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->add(array('foo' => 'bar'));
+
+        $tempDir = sys_get_temp_dir();
+        $dumper = new YamlFileDumper();
+        $dumper->dump($catalogue, array('path' => $tempDir));
+
+        $this->assertEquals(file_get_contents(__DIR__.'/../fixtures/resources.yml'), file_get_contents($tempDir.'/messages.en.yml'));
+
+        unlink($tempDir.'/messages.en.yml');
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/IdentityTranslatorTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/IdentityTranslatorTest.php
new file mode 100644 (file)
index 0000000..435f0c2
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests;
+
+use Symfony\Component\Translation\IdentityTranslator;
+use Symfony\Component\Translation\MessageSelector;
+
+class IdentityTranslatorTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @dataProvider getTransTests
+     */
+    public function testTrans($expected, $id, $parameters)
+    {
+        $translator = new IdentityTranslator(new MessageSelector());
+
+        $this->assertEquals($expected, $translator->trans($id, $parameters));
+    }
+
+    /**
+     * @dataProvider getTransChoiceTests
+     */
+    public function testTransChoice($expected, $id, $number, $parameters)
+    {
+        $translator = new IdentityTranslator(new MessageSelector());
+
+        $this->assertEquals($expected, $translator->transChoice($id, $number, $parameters));
+    }
+
+    // noop
+    public function testGetSetLocale()
+    {
+        $translator = new IdentityTranslator(new MessageSelector());
+        $translator->setLocale('en');
+        $translator->getLocale();
+    }
+
+    public function getTransTests()
+    {
+        return array(
+            array('Symfony2 is great!', 'Symfony2 is great!', array()),
+            array('Symfony2 is awesome!', 'Symfony2 is %what%!', array('%what%' => 'awesome')),
+        );
+    }
+
+    public function getTransChoiceTests()
+    {
+        return array(
+            array('There is 10 apples', '{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples', 10, array('%count%' => 10)),
+        );
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/IntervalTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/IntervalTest.php
new file mode 100644 (file)
index 0000000..075c98b
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests;
+
+use Symfony\Component\Translation\Interval;
+
+class IntervalTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @dataProvider getTests
+     */
+    public function testTest($expected, $number, $interval)
+    {
+        $this->assertEquals($expected, Interval::test($number, $interval));
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testTestException()
+    {
+        Interval::test(1, 'foobar');
+    }
+
+    public function getTests()
+    {
+        return array(
+            array(true, 3, '{1,2, 3 ,4}'),
+            array(false, 10, '{1,2, 3 ,4}'),
+            array(false, 3, '[1,2]'),
+            array(true, 1, '[1,2]'),
+            array(true, 2, '[1,2]'),
+            array(false, 1, ']1,2['),
+            array(false, 2, ']1,2['),
+            array(true, log(0), '[-Inf,2['),
+            array(true, -log(0), '[-2,+Inf]'),
+        );
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/CsvFileLoaderTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/CsvFileLoaderTest.php
new file mode 100644 (file)
index 0000000..59569a3
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use Symfony\Component\Translation\Loader\CsvFileLoader;
+use Symfony\Component\Config\Resource\FileResource;
+
+class CsvFileLoaderTest extends \PHPUnit_Framework_TestCase
+{
+    protected function setUp()
+    {
+        if (!class_exists('Symfony\Component\Config\Loader\Loader')) {
+            $this->markTestSkipped('The "Config" component is not available');
+        }
+    }
+
+    public function testLoad()
+    {
+        $loader = new CsvFileLoader();
+        $resource = __DIR__.'/../fixtures/resources.csv';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+    }
+
+    public function testLoadDoesNothingIfEmpty()
+    {
+        $loader = new CsvFileLoader();
+        $resource = __DIR__.'/../fixtures/empty.csv';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(array(), $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+     */
+    public function testLoadNonExistingResource()
+    {
+        $loader = new CsvFileLoader();
+        $resource = __DIR__.'/../fixtures/not-exists.csv';
+        $loader->load($resource, 'en', 'domain1');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+     */
+    public function testLoadNonLocalResource()
+    {
+        $loader = new CsvFileLoader();
+        $resource = 'http://example.com/resources.csv';
+        $loader->load($resource, 'en', 'domain1');
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/IcuDatFileLoaderTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/IcuDatFileLoaderTest.php
new file mode 100644 (file)
index 0000000..a3bd67a
--- /dev/null
@@ -0,0 +1,72 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use Symfony\Component\Translation\Loader\IcuDatFileLoader;
+use Symfony\Component\Config\Resource\FileResource;
+
+class IcuDatFileLoaderTest extends LocalizedTestCase
+{
+    protected function setUp()
+    {
+        if (!class_exists('Symfony\Component\Config\Loader\Loader')) {
+            $this->markTestSkipped('The "Config" component is not available');
+        }
+
+        if (!extension_loaded('intl')) {
+            $this->markTestSkipped('This test requires intl extension to work.');
+        }
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+     */
+    public function testLoadInvalidResource()
+    {
+        $loader = new IcuDatFileLoader();
+        $loader->load(__DIR__.'/../fixtures/resourcebundle/corrupted/resources', 'es', 'domain2');
+    }
+
+    public function testDatEnglishLoad()
+    {
+        // bundled resource is build using pkgdata command which at least in ICU 4.2 comes in extremely! buggy form
+        // you must specify an temporary build directory which is not the same as current directory and
+        // MUST reside on the same partition. pkgdata -p resources -T /srv -d.packagelist.txt
+        $loader = new IcuDatFileLoader();
+        $resource = __DIR__.'/../fixtures/resourcebundle/dat/resources';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(array('symfony' => 'Symfony 2 is great'), $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals(array(new FileResource($resource.'.dat')), $catalogue->getResources());
+    }
+
+    public function testDatFrenchLoad()
+    {
+        $loader = new IcuDatFileLoader();
+        $resource = __DIR__.'/../fixtures/resourcebundle/dat/resources';
+        $catalogue = $loader->load($resource, 'fr', 'domain1');
+
+        $this->assertEquals(array('symfony' => 'Symfony 2 est génial'), $catalogue->all('domain1'));
+        $this->assertEquals('fr', $catalogue->getLocale());
+        $this->assertEquals(array(new FileResource($resource.'.dat')), $catalogue->getResources());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+     */
+    public function testLoadNonExistingResource()
+    {
+        $loader = new IcuDatFileLoader();
+        $loader->load(__DIR__.'/../fixtures/non-existing.txt', 'en', 'domain1');
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/IcuResFileLoaderTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/IcuResFileLoaderTest.php
new file mode 100644 (file)
index 0000000..233e189
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use Symfony\Component\Translation\Loader\IcuResFileLoader;
+use Symfony\Component\Config\Resource\DirectoryResource;
+
+class IcuResFileLoaderTest extends LocalizedTestCase
+{
+    protected function setUp()
+    {
+        if (!class_exists('Symfony\Component\Config\Loader\Loader')) {
+            $this->markTestSkipped('The "Config" component is not available');
+        }
+
+        if (!extension_loaded('intl')) {
+            $this->markTestSkipped('This test requires intl extension to work.');
+        }
+    }
+
+    public function testLoad()
+    {
+        // resource is build using genrb command
+        $loader = new IcuResFileLoader();
+        $resource = __DIR__.'/../fixtures/resourcebundle/res';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals(array(new DirectoryResource($resource)), $catalogue->getResources());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+     */
+    public function testLoadNonExistingResource()
+    {
+        $loader = new IcuResFileLoader();
+        $loader->load(__DIR__.'/../fixtures/non-existing.txt', 'en', 'domain1');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+     */
+    public function testLoadInvalidResource()
+    {
+        $loader = new IcuResFileLoader();
+        $loader->load(__DIR__.'/../fixtures/resourcebundle/corrupted', 'en', 'domain1');
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/IniFileLoaderTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/IniFileLoaderTest.php
new file mode 100644 (file)
index 0000000..ae1289d
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use Symfony\Component\Translation\Loader\IniFileLoader;
+use Symfony\Component\Config\Resource\FileResource;
+
+class IniFileLoaderTest extends \PHPUnit_Framework_TestCase
+{
+    protected function setUp()
+    {
+        if (!class_exists('Symfony\Component\Config\Loader\Loader')) {
+            $this->markTestSkipped('The "Config" component is not available');
+        }
+    }
+
+    public function testLoad()
+    {
+        $loader = new IniFileLoader();
+        $resource = __DIR__.'/../fixtures/resources.ini';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+    }
+
+    public function testLoadDoesNothingIfEmpty()
+    {
+        $loader = new IniFileLoader();
+        $resource = __DIR__.'/../fixtures/empty.ini';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(array(), $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+     */
+    public function testLoadNonExistingResource()
+    {
+        $loader = new IniFileLoader();
+        $resource = __DIR__.'/../fixtures/non-existing.ini';
+        $loader->load($resource, 'en', 'domain1');
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/LocalizedTestCase.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/LocalizedTestCase.php
new file mode 100644 (file)
index 0000000..9d7c5d7
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+abstract class LocalizedTestCase extends \PHPUnit_Framework_TestCase
+{
+    protected function setUp()
+    {
+        if (!extension_loaded('intl')) {
+            $this->markTestSkipped('The "intl" extension is not available');
+        }
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/MoFileLoaderTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/MoFileLoaderTest.php
new file mode 100644 (file)
index 0000000..c2616bd
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use Symfony\Component\Translation\Loader\MoFileLoader;
+use Symfony\Component\Config\Resource\FileResource;
+
+class MoFileLoaderTest extends \PHPUnit_Framework_TestCase
+{
+    protected function setUp()
+    {
+        if (!class_exists('Symfony\Component\Config\Loader\Loader')) {
+            $this->markTestSkipped('The "Config" component is not available');
+        }
+    }
+
+    public function testLoad()
+    {
+        $loader = new MoFileLoader();
+        $resource = __DIR__.'/../fixtures/resources.mo';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+    }
+
+    public function testLoadPlurals()
+    {
+        $loader = new MoFileLoader();
+        $resource = __DIR__.'/../fixtures/plurals.mo';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(array('foo' => 'bar', 'foos' => '{0} bar|{1} bars'), $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+     */
+    public function testLoadNonExistingResource()
+    {
+        $loader = new MoFileLoader();
+        $resource = __DIR__.'/../fixtures/non-existing.mo';
+        $loader->load($resource, 'en', 'domain1');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+     */
+    public function testLoadInvalidResource()
+    {
+        $loader = new MoFileLoader();
+        $resource = __DIR__.'/../fixtures/empty.mo';
+        $loader->load($resource, 'en', 'domain1');
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/PhpFileLoaderTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/PhpFileLoaderTest.php
new file mode 100644 (file)
index 0000000..5dfe837
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use Symfony\Component\Translation\Loader\PhpFileLoader;
+use Symfony\Component\Config\Resource\FileResource;
+
+class PhpFileLoaderTest extends \PHPUnit_Framework_TestCase
+{
+    protected function setUp()
+    {
+        if (!class_exists('Symfony\Component\Config\Loader\Loader')) {
+            $this->markTestSkipped('The "Config" component is not available');
+        }
+    }
+
+    public function testLoad()
+    {
+        $loader = new PhpFileLoader();
+        $resource = __DIR__.'/../fixtures/resources.php';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+     */
+    public function testLoadNonExistingResource()
+    {
+        $loader = new PhpFileLoader();
+        $resource = __DIR__.'/../fixtures/non-existing.php';
+        $loader->load($resource, 'en', 'domain1');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+     */
+    public function testLoadThrowsAnExceptionIfFileNotLocal()
+    {
+        $loader = new PhpFileLoader();
+        $resource = 'http://example.com/resources.php';
+        $loader->load($resource, 'en', 'domain1');
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/PoFileLoaderTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/PoFileLoaderTest.php
new file mode 100644 (file)
index 0000000..cd3d85a
--- /dev/null
@@ -0,0 +1,79 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use Symfony\Component\Translation\Loader\PoFileLoader;
+use Symfony\Component\Config\Resource\FileResource;
+
+class PoFileLoaderTest extends \PHPUnit_Framework_TestCase
+{
+    protected function setUp()
+    {
+        if (!class_exists('Symfony\Component\Config\Loader\Loader')) {
+            $this->markTestSkipped('The "Config" component is not available');
+        }
+    }
+
+    public function testLoad()
+    {
+        $loader = new PoFileLoader();
+        $resource = __DIR__.'/../fixtures/resources.po';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+    }
+
+    public function testLoadPlurals()
+    {
+        $loader = new PoFileLoader();
+        $resource = __DIR__.'/../fixtures/plurals.po';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(array('foo' => 'bar', 'foos' => 'bar|bars'), $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+    }
+
+    public function testLoadDoesNothingIfEmpty()
+    {
+        $loader = new PoFileLoader();
+        $resource = __DIR__.'/../fixtures/empty.po';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(array(), $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+     */
+    public function testLoadNonExistingResource()
+    {
+        $loader = new PoFileLoader();
+        $resource = __DIR__.'/../fixtures/non-existing.po';
+        $loader->load($resource, 'en', 'domain1');
+    }
+
+    public function testLoadEmptyTranslation()
+    {
+        $loader = new PoFileLoader();
+        $resource = __DIR__.'/../fixtures/empty-translation.po';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(array('foo' => ''), $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/QtFileLoaderTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/QtFileLoaderTest.php
new file mode 100644 (file)
index 0000000..c1dd7b1
--- /dev/null
@@ -0,0 +1,66 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use Symfony\Component\Translation\Loader\QtFileLoader;
+use Symfony\Component\Config\Resource\FileResource;
+
+class QtFileLoaderTest extends \PHPUnit_Framework_TestCase
+{
+    protected function setUp()
+    {
+        if (!class_exists('Symfony\Component\Config\Loader\Loader')) {
+            $this->markTestSkipped('The "Config" component is not available');
+        }
+    }
+
+    public function testLoad()
+    {
+        $loader = new QtFileLoader();
+        $resource = __DIR__.'/../fixtures/resources.ts';
+        $catalogue = $loader->load($resource, 'en', 'resources');
+
+        $this->assertEquals(array('foo' => 'bar'), $catalogue->all('resources'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+     */
+    public function testLoadNonExistingResource()
+    {
+        $loader = new QtFileLoader();
+        $resource = __DIR__.'/../fixtures/non-existing.ts';
+        $loader->load($resource, 'en', 'domain1');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+     */
+    public function testLoadNonLocalResource()
+    {
+        $loader = new QtFileLoader();
+        $resource = 'http://domain1.com/resources.ts';
+        $loader->load($resource, 'en', 'domain1');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+     */
+    public function testLoadInvalidResource()
+    {
+        $loader = new QtFileLoader();
+        $resource = __DIR__.'/../fixtures/invalid-xml-resources.xlf';
+        $loader->load($resource, 'en', 'domain1');
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/XliffFileLoaderTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/XliffFileLoaderTest.php
new file mode 100644 (file)
index 0000000..1d58e0e
--- /dev/null
@@ -0,0 +1,113 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use Symfony\Component\Translation\Loader\XliffFileLoader;
+use Symfony\Component\Config\Resource\FileResource;
+
+class XliffFileLoaderTest extends \PHPUnit_Framework_TestCase
+{
+    protected function setUp()
+    {
+        if (!class_exists('Symfony\Component\Config\Loader\Loader')) {
+            $this->markTestSkipped('The "Config" component is not available');
+        }
+    }
+
+    public function testLoad()
+    {
+        $loader = new XliffFileLoader();
+        $resource = __DIR__.'/../fixtures/resources.xlf';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+    }
+
+    public function testLoadWithResname()
+    {
+        $loader = new XliffFileLoader();
+        $catalogue = $loader->load(__DIR__.'/../fixtures/resname.xlf', 'en', 'domain1');
+
+        $this->assertEquals(array('foo' => 'bar', 'bar' => 'baz', 'baz' => 'foo'), $catalogue->all('domain1'));
+    }
+
+    public function testIncompleteResource()
+    {
+        $loader = new XliffFileLoader();
+        $catalogue = $loader->load(__DIR__.'/../fixtures/resources.xlf', 'en', 'domain1');
+
+        $this->assertEquals(array('foo' => 'bar', 'key' => '', 'test' => 'with'), $catalogue->all('domain1'));
+        $this->assertFalse($catalogue->has('extra', 'domain1'));
+    }
+
+    public function testEncoding()
+    {
+        if (!function_exists('iconv') && !function_exists('mb_convert_encoding')) {
+            $this->markTestSkipped('The iconv and mbstring extensions are not available.');
+        }
+
+        $loader = new XliffFileLoader();
+        $catalogue = $loader->load(__DIR__.'/../fixtures/encoding.xlf', 'en', 'domain1');
+
+        $this->assertEquals(utf8_decode('föö'), $catalogue->get('bar', 'domain1'));
+        $this->assertEquals(utf8_decode('bär'), $catalogue->get('foo', 'domain1'));
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+     */
+    public function testLoadInvalidResource()
+    {
+        $loader = new XliffFileLoader();
+        $loader->load(__DIR__.'/../fixtures/resources.php', 'en', 'domain1');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+     */
+    public function testLoadResourceDoesNotValidate()
+    {
+        $loader = new XliffFileLoader();
+        $loader->load(__DIR__.'/../fixtures/non-valid.xlf', 'en', 'domain1');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+     */
+    public function testLoadNonExistingResource()
+    {
+        $loader = new XliffFileLoader();
+        $resource = __DIR__.'/../fixtures/non-existing.xlf';
+        $loader->load($resource, 'en', 'domain1');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+     */
+    public function testLoadThrowsAnExceptionIfFileNotLocal()
+    {
+        $loader = new XliffFileLoader();
+        $resource = 'http://example.com/resources.xlf';
+        $loader->load($resource, 'en', 'domain1');
+    }
+
+    /**
+     * @expectedException        \Symfony\Component\Translation\Exception\InvalidResourceException
+     * @expectedExceptionMessage Document types are not allowed.
+     */
+    public function testDocTypeIsNotAllowed()
+    {
+        $loader = new XliffFileLoader();
+        $loader->load(__DIR__.'/../fixtures/withdoctype.xlf', 'en', 'domain1');
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/YamlFileLoaderTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/YamlFileLoaderTest.php
new file mode 100644 (file)
index 0000000..511b127
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Loader;
+
+use Symfony\Component\Translation\Loader\YamlFileLoader;
+use Symfony\Component\Config\Resource\FileResource;
+
+class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
+{
+    protected function setUp()
+    {
+        if (!class_exists('Symfony\Component\Config\Loader\Loader')) {
+            $this->markTestSkipped('The "Config" component is not available');
+        }
+
+        if (!class_exists('Symfony\Component\Yaml\Yaml')) {
+            $this->markTestSkipped('The "Yaml" component is not available');
+        }
+    }
+
+    public function testLoad()
+    {
+        $loader = new YamlFileLoader();
+        $resource = __DIR__.'/../fixtures/resources.yml';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+    }
+
+    public function testLoadDoesNothingIfEmpty()
+    {
+        $loader = new YamlFileLoader();
+        $resource = __DIR__.'/../fixtures/empty.yml';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(array(), $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+     */
+    public function testLoadNonExistingResource()
+    {
+        $loader = new YamlFileLoader();
+        $resource = __DIR__.'/../fixtures/non-existing.yml';
+        $loader->load($resource, 'en', 'domain1');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+     */
+    public function testLoadThrowsAnExceptionIfFileNotLocal()
+    {
+        $loader = new YamlFileLoader();
+        $resource = 'http://example.com/resources.yml';
+        $loader->load($resource, 'en', 'domain1');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+     */
+    public function testLoadThrowsAnExceptionIfNotAnArray()
+    {
+        $loader = new YamlFileLoader();
+        $resource = __DIR__.'/../fixtures/non-valid.yml';
+        $loader->load($resource, 'en', 'domain1');
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/MessageCatalogueTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/MessageCatalogueTest.php
new file mode 100644 (file)
index 0000000..aa6f870
--- /dev/null
@@ -0,0 +1,212 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+class MessageCatalogueTest extends \PHPUnit_Framework_TestCase
+{
+    public function testGetLocale()
+    {
+        $catalogue = new MessageCatalogue('en');
+
+        $this->assertEquals('en', $catalogue->getLocale());
+    }
+
+    public function testGetDomains()
+    {
+        $catalogue = new MessageCatalogue('en', array('domain1' => array(), 'domain2' => array()));
+
+        $this->assertEquals(array('domain1', 'domain2'), $catalogue->getDomains());
+    }
+
+    public function testAll()
+    {
+        $catalogue = new MessageCatalogue('en', $messages = array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
+
+        $this->assertEquals(array('foo' => 'foo'), $catalogue->all('domain1'));
+        $this->assertEquals(array(), $catalogue->all('domain88'));
+        $this->assertEquals($messages, $catalogue->all());
+    }
+
+    public function testHas()
+    {
+        $catalogue = new MessageCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
+
+        $this->assertTrue($catalogue->has('foo', 'domain1'));
+        $this->assertFalse($catalogue->has('bar', 'domain1'));
+        $this->assertFalse($catalogue->has('foo', 'domain88'));
+    }
+
+    public function testGetSet()
+    {
+        $catalogue = new MessageCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
+        $catalogue->set('foo1', 'foo1', 'domain1');
+
+        $this->assertEquals('foo', $catalogue->get('foo', 'domain1'));
+        $this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
+    }
+
+    public function testAdd()
+    {
+        $catalogue = new MessageCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
+        $catalogue->add(array('foo1' => 'foo1'), 'domain1');
+
+        $this->assertEquals('foo', $catalogue->get('foo', 'domain1'));
+        $this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
+
+        $catalogue->add(array('foo' => 'bar'), 'domain1');
+        $this->assertEquals('bar', $catalogue->get('foo', 'domain1'));
+        $this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
+
+        $catalogue->add(array('foo' => 'bar'), 'domain88');
+        $this->assertEquals('bar', $catalogue->get('foo', 'domain88'));
+    }
+
+    public function testReplace()
+    {
+        $catalogue = new MessageCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
+        $catalogue->replace($messages = array('foo1' => 'foo1'), 'domain1');
+
+        $this->assertEquals($messages, $catalogue->all('domain1'));
+    }
+
+    public function testAddCatalogue()
+    {
+        if (!class_exists('Symfony\Component\Config\Loader\Loader')) {
+            $this->markTestSkipped('The "Config" component is not available');
+        }
+
+        $r = $this->getMock('Symfony\Component\Config\Resource\ResourceInterface');
+        $r->expects($this->any())->method('__toString')->will($this->returnValue('r'));
+
+        $r1 = $this->getMock('Symfony\Component\Config\Resource\ResourceInterface');
+        $r1->expects($this->any())->method('__toString')->will($this->returnValue('r1'));
+
+        $catalogue = new MessageCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
+        $catalogue->addResource($r);
+
+        $catalogue1 = new MessageCatalogue('en', array('domain1' => array('foo1' => 'foo1')));
+        $catalogue1->addResource($r1);
+
+        $catalogue->addCatalogue($catalogue1);
+
+        $this->assertEquals('foo', $catalogue->get('foo', 'domain1'));
+        $this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
+
+        $this->assertEquals(array($r, $r1), $catalogue->getResources());
+    }
+
+    public function testAddFallbackCatalogue()
+    {
+        if (!class_exists('Symfony\Component\Config\Loader\Loader')) {
+            $this->markTestSkipped('The "Config" component is not available');
+        }
+
+        $r = $this->getMock('Symfony\Component\Config\Resource\ResourceInterface');
+        $r->expects($this->any())->method('__toString')->will($this->returnValue('r'));
+
+        $r1 = $this->getMock('Symfony\Component\Config\Resource\ResourceInterface');
+        $r1->expects($this->any())->method('__toString')->will($this->returnValue('r1'));
+
+        $catalogue = new MessageCatalogue('en_US', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
+        $catalogue->addResource($r);
+
+        $catalogue1 = new MessageCatalogue('en', array('domain1' => array('foo' => 'bar', 'foo1' => 'foo1')));
+        $catalogue1->addResource($r1);
+
+        $catalogue->addFallbackCatalogue($catalogue1);
+
+        $this->assertEquals('foo', $catalogue->get('foo', 'domain1'));
+        $this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
+
+        $this->assertEquals(array($r, $r1), $catalogue->getResources());
+    }
+
+    /**
+     * @expectedException LogicException
+     */
+    public function testAddFallbackCatalogueWithCircularReference()
+    {
+        $main = new MessageCatalogue('en_US');
+        $fallback = new MessageCatalogue('fr_FR');
+
+        $fallback->addFallbackCatalogue($main);
+        $main->addFallbackCatalogue($fallback);
+    }
+
+    /**
+     * @expectedException LogicException
+     */
+    public function testAddCatalogueWhenLocaleIsNotTheSameAsTheCurrentOne()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->addCatalogue(new MessageCatalogue('fr', array()));
+    }
+
+    public function testGetAddResource()
+    {
+        if (!class_exists('Symfony\Component\Config\Loader\Loader')) {
+            $this->markTestSkipped('The "Config" component is not available');
+        }
+
+        $catalogue = new MessageCatalogue('en');
+        $r = $this->getMock('Symfony\Component\Config\Resource\ResourceInterface');
+        $r->expects($this->any())->method('__toString')->will($this->returnValue('r'));
+        $catalogue->addResource($r);
+        $catalogue->addResource($r);
+        $r1 = $this->getMock('Symfony\Component\Config\Resource\ResourceInterface');
+        $r1->expects($this->any())->method('__toString')->will($this->returnValue('r1'));
+        $catalogue->addResource($r1);
+
+        $this->assertEquals(array($r, $r1), $catalogue->getResources());
+    }
+
+    public function testMetadataDelete()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $this->assertEquals(array(), $catalogue->getMetadata('', ''), 'Metadata is empty');
+        $catalogue->deleteMetadata('key', 'messages');
+        $catalogue->deleteMetadata('', 'messages');
+        $catalogue->deleteMetadata();
+    }
+
+    public function testMetadataSetGetDelete()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->setMetadata('key', 'value');
+        $this->assertEquals('value', $catalogue->getMetadata('key', 'messages'), "Metadata 'key' = 'value'");
+
+        $catalogue->setMetadata('key2', array());
+        $this->assertEquals(array(), $catalogue->getMetadata('key2', 'messages'), 'Metadata key2 is array');
+
+        $catalogue->deleteMetadata('key2', 'messages');
+        $this->assertEquals(null, $catalogue->getMetadata('key2', 'messages'), 'Metadata key2 should is deleted.');
+
+        $catalogue->deleteMetadata('key2', 'domain');
+        $this->assertEquals(null, $catalogue->getMetadata('key2', 'domain'), 'Metadata key2 should is deleted.');
+    }
+
+    public function testMetadataMerge()
+    {
+        $cat1 = new MessageCatalogue('en');
+        $cat1->setMetadata('a', 'b');
+        $this->assertEquals(array('messages' => array('a' => 'b')), $cat1->getMetadata('', ''), 'Cat1 contains messages metadata.');
+
+        $cat2 = new MessageCatalogue('en');
+        $cat2->setMetadata('b', 'c', 'domain');
+        $this->assertEquals(array('domain' => array('b' => 'c')), $cat2->getMetadata('', ''), 'Cat2 contains domain metadata.');
+
+        $cat1->addCatalogue($cat2);
+        $this->assertEquals(array('messages' => array('a' => 'b'), 'domain' => array('b' => 'c')), $cat1->getMetadata('', ''), 'Cat1 contains merged metadata.');
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/MessageSelectorTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/MessageSelectorTest.php
new file mode 100644 (file)
index 0000000..7495629
--- /dev/null
@@ -0,0 +1,80 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests;
+
+use Symfony\Component\Translation\MessageSelector;
+
+class MessageSelectorTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @dataProvider getChooseTests
+     */
+    public function testChoose($expected, $id, $number)
+    {
+        $selector = new MessageSelector();
+
+        $this->assertEquals($expected, $selector->choose($id, $number, 'en'));
+    }
+
+    /**
+     * @expectedException InvalidArgumentException
+     */
+    public function testChooseWhenNoEnoughChoices()
+    {
+        $selector = new MessageSelector();
+
+        $selector->choose('foo', 10, 'en');
+    }
+
+    public function getChooseTests()
+    {
+        return array(
+            array('There is no apples', '{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples', 0),
+            array('There is no apples', '{0}     There is no apples|{1} There is one apple|]1,Inf] There is %count% apples', 0),
+            array('There is no apples', '{0}There is no apples|{1} There is one apple|]1,Inf] There is %count% apples', 0),
+
+            array('There is one apple', '{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples', 1),
+
+            array('There is %count% apples', '{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples', 10),
+            array('There is %count% apples', '{0} There is no apples|{1} There is one apple|]1,Inf]There is %count% apples', 10),
+            array('There is %count% apples', '{0} There is no apples|{1} There is one apple|]1,Inf]     There is %count% apples', 10),
+
+            array('There is %count% apples', 'There is one apple|There is %count% apples', 0),
+            array('There is one apple', 'There is one apple|There is %count% apples', 1),
+            array('There is %count% apples', 'There is one apple|There is %count% apples', 10),
+
+            array('There is %count% apples', 'one: There is one apple|more: There is %count% apples', 0),
+            array('There is one apple', 'one: There is one apple|more: There is %count% apples', 1),
+            array('There is %count% apples', 'one: There is one apple|more: There is %count% apples', 10),
+
+            array('There is no apples', '{0} There is no apples|one: There is one apple|more: There is %count% apples', 0),
+            array('There is one apple', '{0} There is no apples|one: There is one apple|more: There is %count% apples', 1),
+            array('There is %count% apples', '{0} There is no apples|one: There is one apple|more: There is %count% apples', 10),
+
+            array('', '{0}|{1} There is one apple|]1,Inf] There is %count% apples', 0),
+            array('', '{0} There is no apples|{1}|]1,Inf] There is %count% apples', 1),
+
+            // Indexed only tests which are Gettext PoFile* compatible strings.
+            array('There are %count% apples', 'There is one apple|There are %count% apples', 0),
+            array('There is one apple', 'There is one apple|There are %count% apples', 1),
+            array('There are %count% apples', 'There is one apple|There are %count% apples', 2),
+
+            // Tests for float numbers
+            array('There is almost one apple', '{0} There is no apples|]0,1[ There is almost one apple|{1} There is one apple|[1,Inf] There is more than one apple', 0.7),
+            array('There is one apple', '{0} There is no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1),
+            array('There is more than one apple', '{0} There is no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1.7),
+            array('There is no apples', '{0} There is no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0),
+            array('There is no apples', '{0} There is no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0.0),
+            array('There is no apples', '{0.0} There is no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0),
+        );
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/PluralizationRulesTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/PluralizationRulesTest.php
new file mode 100644 (file)
index 0000000..26f9e2f
--- /dev/null
@@ -0,0 +1,124 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests;
+
+use Symfony\Component\Translation\PluralizationRules;
+
+/**
+ * Test should cover all languages mentioned on http://translate.sourceforge.net/wiki/l10n/pluralforms
+ * and Plural forms mentioned on http://www.gnu.org/software/gettext/manual/gettext.html#Plural-forms
+ *
+ * See also https://developer.mozilla.org/en/Localization_and_Plurals which mentions 15 rules having a maximum of 6 forms.
+ * The mozilla code is also interesting to check for.
+ *
+ * As mentioned by chx http://drupal.org/node/1273968 we can cover all by testing number from 0 to 199
+ *
+ * The goal to cover all languages is to far fetched so this test case is smaller.
+ *
+ * @author Clemens Tolboom clemens@build2be.nl
+ */
+class PluralizationRulesTest  extends \PHPUnit_Framework_TestCase
+{
+
+    /**
+     * We test failed langcode here.
+     *
+     * TODO: The languages mentioned in the data provide need to get fixed somehow within PluralizationRules.
+     *
+     * @dataProvider failingLangcodes
+     */
+    public function testFailedLangcodes($nplural, $langCodes)
+    {
+        $matrix = $this->generateTestData($nplural, $langCodes);
+        $this->validateMatrix($nplural, $matrix, false);
+    }
+
+    /**
+     * @dataProvider successLangcodes
+     */
+    public function testLangcodes($nplural, $langCodes)
+    {
+        $matrix = $this->generateTestData($nplural, $langCodes);
+        $this->validateMatrix($nplural, $matrix);
+    }
+
+    /**
+     * This array should contain all currently known langcodes.
+     *
+     * As it is impossible to have this ever complete we should try as hard as possible to have it almost complete.
+     *
+     * @return type
+     */
+    public function successLangcodes()
+    {
+      return array(
+        array('1' , array('ay','bo', 'cgg','dz','id', 'ja', 'jbo', 'ka','kk','km','ko','ky')),
+        array('2' , array('nl', 'fr', 'en', 'de', 'de_GE')),
+        array('3' , array('be','bs','cs','hr')),
+        array('4' , array('cy','mt', 'sl')),
+        array('5' , array()),
+        array('6' , array('ar')),
+      );
+    }
+
+    /**
+     * This array should be at least empty within the near future.
+     *
+     * This both depends on a complete list trying to add above as understanding
+     * the plural rules of the current failing languages.
+     *
+     * @return array with nplural together with langcodes
+     */
+    public function failingLangcodes()
+    {
+      return array(
+        array('1' , array('fa')),
+        array('2' , array('jbo')),
+        array('3' , array('cbs')),
+        array('4' , array('gd','kw')),
+        array('5' , array('ga')),
+        array('6' , array()),
+      );
+    }
+
+    /**
+     * We validate only on the plural coverage. Thus the real rules is not tested.
+     *
+     * @param string  $nplural       plural expected
+     * @param array   $matrix        containing langcodes and their plural index values.
+     * @param boolean $expectSuccess
+     */
+    protected function validateMatrix($nplural, $matrix, $expectSuccess = true)
+    {
+        foreach ($matrix as $langCode => $data) {
+            $indexes = array_flip($data);
+            if ($expectSuccess) {
+                $this->assertEquals($nplural, count($indexes), "Langcode '$langCode' has '$nplural' plural forms.");
+            } else {
+                $this->assertNotEquals((int) $nplural, count($indexes), "Langcode '$langCode' has '$nplural' plural forms.");
+            }
+        }
+    }
+
+    protected function generateTestData($plural, $langCodes)
+    {
+        $matrix = array();
+        foreach ($langCodes as $langCode) {
+            for ($count=0; $count<200; $count++) {
+                $plural = PluralizationRules::get($count, $langCode);
+                $matrix[$langCode][$count] = $plural;
+            }
+        }
+
+        return $matrix;
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/TranslatorTest.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/TranslatorTest.php
new file mode 100644 (file)
index 0000000..fb843d7
--- /dev/null
@@ -0,0 +1,306 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests;
+
+use Symfony\Component\Translation\Translator;
+use Symfony\Component\Translation\MessageSelector;
+use Symfony\Component\Translation\Loader\ArrayLoader;
+
+class TranslatorTest extends \PHPUnit_Framework_TestCase
+{
+    public function testSetGetLocale()
+    {
+        $translator = new Translator('en', new MessageSelector());
+
+        $this->assertEquals('en', $translator->getLocale());
+
+        $translator->setLocale('fr');
+        $this->assertEquals('fr', $translator->getLocale());
+    }
+
+    public function testSetFallbackLocales()
+    {
+        $translator = new Translator('en', new MessageSelector());
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', array('foo' => 'foofoo'), 'en');
+        $translator->addResource('array', array('bar' => 'foobar'), 'fr');
+
+        // force catalogue loading
+        $translator->trans('bar');
+
+        $translator->setFallbackLocales(array('fr'));
+        $this->assertEquals('foobar', $translator->trans('bar'));
+    }
+
+    public function testSetFallbackLocalesMultiple()
+    {
+        $translator = new Translator('en', new MessageSelector());
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', array('foo' => 'foo (en)'), 'en');
+        $translator->addResource('array', array('bar' => 'bar (fr)'), 'fr');
+
+        // force catalogue loading
+        $translator->trans('bar');
+
+        $translator->setFallbackLocales(array('fr_FR', 'fr'));
+        $this->assertEquals('bar (fr)', $translator->trans('bar'));
+    }
+
+    public function testTransWithFallbackLocale()
+    {
+        $translator = new Translator('fr_FR', new MessageSelector());
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', array('foo' => 'foofoo'), 'en_US');
+        $translator->addResource('array', array('bar' => 'foobar'), 'en');
+
+        $translator->setFallbackLocales(array('en'));
+
+        $this->assertEquals('foobar', $translator->trans('bar'));
+    }
+
+    public function testAddResourceAfterTrans()
+    {
+        $translator = new Translator('fr', new MessageSelector());
+        $translator->addLoader('array', new ArrayLoader());
+
+        $translator->setFallbackLocale(array('en'));
+
+        $translator->addResource('array', array('foo' => 'foofoo'), 'en');
+        $this->assertEquals('foofoo', $translator->trans('foo'));
+
+        $translator->addResource('array', array('bar' => 'foobar'), 'en');
+        $this->assertEquals('foobar', $translator->trans('bar'));
+    }
+
+    /**
+     * @dataProvider      getTransFileTests
+     * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+     */
+    public function testTransWithoutFallbackLocaleFile($format, $loader)
+    {
+        $loaderClass = 'Symfony\\Component\\Translation\\Loader\\'.$loader;
+        $translator = new Translator('en', new MessageSelector());
+        $translator->addLoader($format, new $loaderClass());
+        $translator->addResource($format, __DIR__.'/fixtures/non-existing', 'en');
+        $translator->addResource($format, __DIR__.'/fixtures/resources.'.$format, 'en');
+
+        // force catalogue loading
+        $translator->trans('foo');
+    }
+
+    /**
+     * @dataProvider getTransFileTests
+     */
+    public function testTransWithFallbackLocaleFile($format, $loader)
+    {
+        $loaderClass = 'Symfony\\Component\\Translation\\Loader\\'.$loader;
+        $translator = new Translator('en_GB', new MessageSelector());
+        $translator->addLoader($format, new $loaderClass());
+        $translator->addResource($format, __DIR__.'/fixtures/non-existing', 'en_GB');
+        $translator->addResource($format, __DIR__.'/fixtures/resources.'.$format, 'en', 'resources');
+
+        $this->assertEquals('bar', $translator->trans('foo', array(), 'resources'));
+    }
+
+    public function testTransWithFallbackLocaleBis()
+    {
+        $translator = new Translator('en_US', new MessageSelector());
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', array('foo' => 'foofoo'), 'en_US');
+        $translator->addResource('array', array('bar' => 'foobar'), 'en');
+        $this->assertEquals('foobar', $translator->trans('bar'));
+    }
+
+    public function testTransWithFallbackLocaleTer()
+    {
+        $translator = new Translator('fr_FR', new MessageSelector());
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', array('foo' => 'foo (en_US)'), 'en_US');
+        $translator->addResource('array', array('bar' => 'bar (en)'), 'en');
+
+        $translator->setFallbackLocales(array('en_US', 'en'));
+
+        $this->assertEquals('foo (en_US)', $translator->trans('foo'));
+        $this->assertEquals('bar (en)', $translator->trans('bar'));
+    }
+
+    public function testTransNonExistentWithFallback()
+    {
+        $translator = new Translator('fr', new MessageSelector());
+        $translator->setFallbackLocales(array('en'));
+        $translator->addLoader('array', new ArrayLoader());
+        $this->assertEquals('non-existent', $translator->trans('non-existent'));
+    }
+
+    /**
+     * @expectedException RuntimeException
+     */
+    public function testWhenAResourceHasNoRegisteredLoader()
+    {
+        $translator = new Translator('en', new MessageSelector());
+        $translator->addResource('array', array('foo' => 'foofoo'), 'en');
+
+        $translator->trans('foo');
+    }
+
+    /**
+     * @dataProvider getTransTests
+     */
+    public function testTrans($expected, $id, $translation, $parameters, $locale, $domain)
+    {
+        $translator = new Translator('en', new MessageSelector());
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', array((string) $id => $translation), $locale, $domain);
+
+        $this->assertEquals($expected, $translator->trans($id, $parameters, $domain, $locale));
+    }
+
+    /**
+     * @dataProvider getFlattenedTransTests
+     */
+    public function testFlattenedTrans($expected, $messages, $id)
+    {
+        $translator = new Translator('en', new MessageSelector());
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', $messages, 'fr', '');
+
+        $this->assertEquals($expected, $translator->trans($id, array(), '', 'fr'));
+    }
+
+    /**
+     * @dataProvider getTransChoiceTests
+     */
+    public function testTransChoice($expected, $id, $translation, $number, $parameters, $locale, $domain)
+    {
+        $translator = new Translator('en', new MessageSelector());
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', array((string) $id => $translation), $locale, $domain);
+
+        $this->assertEquals($expected, $translator->transChoice($id, $number, $parameters, $domain, $locale));
+    }
+
+    public function getTransFileTests()
+    {
+        return array(
+            array('csv', 'CsvFileLoader'),
+            array('ini', 'IniFileLoader'),
+            array('mo', 'MoFileLoader'),
+            array('po', 'PoFileLoader'),
+            array('php', 'PhpFileLoader'),
+            array('ts', 'QtFileLoader'),
+            array('xlf', 'XliffFileLoader'),
+            array('yml', 'YamlFileLoader'),
+        );
+    }
+
+    public function getTransTests()
+    {
+        return array(
+            array('Symfony2 est super !', 'Symfony2 is great!', 'Symfony2 est super !', array(), 'fr', ''),
+            array('Symfony2 est awesome !', 'Symfony2 is %what%!', 'Symfony2 est %what% !', array('%what%' => 'awesome'), 'fr', ''),
+            array('Symfony2 est super !', new String('Symfony2 is great!'), 'Symfony2 est super !', array(), 'fr', ''),
+        );
+    }
+
+    public function getFlattenedTransTests()
+    {
+        $messages = array(
+            'symfony2' => array(
+                'is' => array(
+                    'great' => 'Symfony2 est super!'
+                )
+            ),
+            'foo' => array(
+                'bar' => array(
+                    'baz' => 'Foo Bar Baz'
+                ),
+                'baz' => 'Foo Baz',
+            ),
+        );
+
+        return array(
+            array('Symfony2 est super!', $messages, 'symfony2.is.great'),
+            array('Foo Bar Baz', $messages, 'foo.bar.baz'),
+            array('Foo Baz', $messages, 'foo.baz'),
+        );
+    }
+
+    public function getTransChoiceTests()
+    {
+        return array(
+            array('Il y a 0 pomme', '{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, array('%count%' => 0), 'fr', ''),
+            array('Il y a 1 pomme', '{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 1, array('%count%' => 1), 'fr', ''),
+            array('Il y a 10 pommes', '{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 10, array('%count%' => 10), 'fr', ''),
+
+            array('Il y a 0 pomme', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 0, array('%count%' => 0), 'fr', ''),
+            array('Il y a 1 pomme', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 1, array('%count%' => 1), 'fr', ''),
+            array('Il y a 10 pommes', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 10, array('%count%' => 10), 'fr', ''),
+
+            array('Il y a 0 pomme', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 0, array('%count%' => 0), 'fr', ''),
+            array('Il y a 1 pomme', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 1, array('%count%' => 1), 'fr', ''),
+            array('Il y a 10 pommes', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 10, array('%count%' => 10), 'fr', ''),
+
+            array('Il n\'y a aucune pomme', '{0} There is no apple|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 0, array('%count%' => 0), 'fr', ''),
+            array('Il y a 1 pomme', '{0} There is no apple|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 1, array('%count%' => 1), 'fr', ''),
+            array('Il y a 10 pommes', '{0} There is no apple|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 10, array('%count%' => 10), 'fr', ''),
+
+            array('Il y a 0 pomme', new String('{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples'), '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, array('%count%' => 0), 'fr', ''),
+        );
+    }
+
+    public function testTransChoiceFallback()
+    {
+        $translator = new Translator('ru', new MessageSelector());
+        $translator->setFallbackLocales(array('en'));
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', array('some_message2' => 'one thing|%count% things'), 'en');
+
+        $this->assertEquals('10 things', $translator->transChoice('some_message2', 10, array('%count%' => 10)));
+    }
+
+    public function testTransChoiceFallbackBis()
+    {
+        $translator = new Translator('ru', new MessageSelector());
+        $translator->setFallbackLocales(array('en_US', 'en'));
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', array('some_message2' => 'one thing|%count% things'), 'en_US');
+
+        $this->assertEquals('10 things', $translator->transChoice('some_message2', 10, array('%count%' => 10)));
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testTransChoiceFallbackWithNoTranslation()
+    {
+        $translator = new Translator('ru', new MessageSelector());
+        $translator->setFallbackLocales(array('en'));
+        $translator->addLoader('array', new ArrayLoader());
+
+        $this->assertEquals('10 things', $translator->transChoice('some_message2', 10, array('%count%' => 10)));
+    }
+}
+
+class String
+{
+    protected $str;
+
+    public function __construct($str)
+    {
+        $this->str = $str;
+    }
+
+    public function __toString()
+    {
+        return $this->str;
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty-translation.po b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty-translation.po
new file mode 100644 (file)
index 0000000..ff6f22a
--- /dev/null
@@ -0,0 +1,3 @@
+msgid "foo"
+msgstr ""
+
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty.csv b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty.csv
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty.ini b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty.ini
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty.mo b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty.mo
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty.po b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty.po
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty.yml b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty.yml
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/encoding.xlf b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/encoding.xlf
new file mode 100644 (file)
index 0000000..6be901b
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
+  <file source-language="en" datatype="plaintext" original="file.ext">
+    <body>
+      <trans-unit id="1" resname="foo">
+        <source>foo</source>
+        <target>bär</target>
+      </trans-unit>
+      <trans-unit id="2" resname="bar">
+        <source>bar</source>
+        <target>föö</target>
+      </trans-unit>
+    </body>
+  </file>
+</xliff>
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/invalid-xml-resources.xlf b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/invalid-xml-resources.xlf
new file mode 100644 (file)
index 0000000..7bf6c98
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="1">
+                <source>foo</source>
+                <target>bar
+            </trans-unit>
+            <trans-unit id="2">
+                <source>extra</source>
+            </trans-unit>
+            <trans-unit id="3">
+                <source>key</source>
+                <target></target>
+            </trans-unit>
+            <trans-unit id="4">
+                <source>test</source>
+                <target>with</target>
+                <note>note</note>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/non-valid.xlf b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/non-valid.xlf
new file mode 100644 (file)
index 0000000..734fc97
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit>
+                <source>foo</source>
+                <target>bar</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/non-valid.yml b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/non-valid.yml
new file mode 100644 (file)
index 0000000..257cc56
--- /dev/null
@@ -0,0 +1 @@
+foo
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/plurals.mo b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/plurals.mo
new file mode 100644 (file)
index 0000000..6445e77
Binary files /dev/null and b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/plurals.mo differ
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/plurals.po b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/plurals.po
new file mode 100644 (file)
index 0000000..439c41a
--- /dev/null
@@ -0,0 +1,5 @@
+msgid "foo"
+msgid_plural "foos"
+msgstr[0] "bar"
+msgstr[1] "bars"
+
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resname.xlf b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resname.xlf
new file mode 100644 (file)
index 0000000..2df16af
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
+  <file source-language="en" datatype="plaintext" original="file.ext">
+    <body>
+      <trans-unit id="1" resname="foo">
+        <source></source>
+        <target>bar</target>
+      </trans-unit>
+      <trans-unit id="2" resname="bar">
+        <source>bar source</source>
+        <target>baz</target>
+      </trans-unit>
+      <trans-unit id="3">
+        <source>baz</source>
+        <target>foo</target>
+      </trans-unit>
+    </body>
+  </file>
+</xliff>
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/corrupted/resources.dat b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/corrupted/resources.dat
new file mode 100644 (file)
index 0000000..391250c
--- /dev/null
@@ -0,0 +1 @@
+XXX
\ No newline at end of file
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/en.res b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/en.res
new file mode 100644 (file)
index 0000000..1fc1436
Binary files /dev/null and b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/en.res differ
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/en.txt b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/en.txt
new file mode 100644 (file)
index 0000000..c04a4e8
--- /dev/null
@@ -0,0 +1,3 @@
+en{
+    symfony{"Symfony 2 is great"}
+}
\ No newline at end of file
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/fr.res b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/fr.res
new file mode 100644 (file)
index 0000000..f584160
Binary files /dev/null and b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/fr.res differ
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/fr.txt b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/fr.txt
new file mode 100644 (file)
index 0000000..7e84f67
--- /dev/null
@@ -0,0 +1,3 @@
+fr{
+    symfony{"Symfony 2 est génial"}
+}
\ No newline at end of file
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/packagelist.txt b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/packagelist.txt
new file mode 100644 (file)
index 0000000..c5783ed
--- /dev/null
@@ -0,0 +1,2 @@
+en.res
+fr.res
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/resources.dat b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/resources.dat
new file mode 100644 (file)
index 0000000..563b0ea
Binary files /dev/null and b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/resources.dat differ
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/res/en.res b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/res/en.res
new file mode 100644 (file)
index 0000000..ad894a9
Binary files /dev/null and b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/res/en.res differ
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources-clean.xlf b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources-clean.xlf
new file mode 100644 (file)
index 0000000..464b079
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
+  <file source-language="en" datatype="plaintext" original="file.ext">
+    <body>
+      <trans-unit id="acbd18db4cc2f85cedef654fccc4a4d8" resname="foo">
+        <source>foo</source>
+        <target>bar</target>
+      </trans-unit>
+      <trans-unit id="3c6e0b8a9c15224a8228b9a98ca1531d" resname="key">
+        <source>key</source>
+        <target></target>
+      </trans-unit>
+    </body>
+  </file>
+</xliff>
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.csv b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.csv
new file mode 100644 (file)
index 0000000..374b9eb
--- /dev/null
@@ -0,0 +1,4 @@
+"foo"; "bar"
+#"bar"; "foo"
+"incorrect"; "number"; "columns"; "will"; "be"; "ignored"
+"incorrect"
\ No newline at end of file
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.ini b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.ini
new file mode 100644 (file)
index 0000000..4953062
--- /dev/null
@@ -0,0 +1 @@
+foo="bar"
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.mo b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.mo
new file mode 100644 (file)
index 0000000..0a96602
Binary files /dev/null and b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.mo differ
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.php b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.php
new file mode 100644 (file)
index 0000000..c291398
--- /dev/null
@@ -0,0 +1,5 @@
+<?php
+
+return array (
+  'foo' => 'bar',
+);
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.po b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.po
new file mode 100644 (file)
index 0000000..da0d5f4
--- /dev/null
@@ -0,0 +1,2 @@
+msgid "foo"
+msgstr "bar"
\ No newline at end of file
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.ts b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.ts
new file mode 100644 (file)
index 0000000..40e1852
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TS>
+  <context>
+    <name>resources</name>
+    <message>
+      <source>foo</source>
+      <translation>bar</translation>
+    </message>
+  </context>
+</TS>
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.xlf b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.xlf
new file mode 100644 (file)
index 0000000..b0e5988
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
+  <file source-language="en" datatype="plaintext" original="file.ext">
+    <body>
+      <trans-unit id="1">
+        <source>foo</source>
+        <target>bar</target>
+      </trans-unit>
+      <trans-unit id="2">
+        <source>extra</source>
+      </trans-unit>
+      <trans-unit id="3">
+        <source>key</source>
+        <target></target>
+      </trans-unit>
+      <trans-unit id="4">
+        <source>test</source>
+        <target>with</target>
+        <note>note</note>
+      </trans-unit>
+    </body>
+  </file>
+</xliff>
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.yml b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.yml
new file mode 100644 (file)
index 0000000..20e9ff3
--- /dev/null
@@ -0,0 +1 @@
+foo: bar
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/valid.csv b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/valid.csv
new file mode 100644 (file)
index 0000000..59882e5
--- /dev/null
@@ -0,0 +1,4 @@
+foo;bar
+bar;"foo
+foo"
+"foo;foo";bar
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/withdoctype.xlf b/vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/withdoctype.xlf
new file mode 100644 (file)
index 0000000..f83e834
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<!DOCTYPE foo>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="1">
+                <source>foo</source>
+                <target>bar</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Translator.php b/vendor/symfony/translation/Symfony/Component/Translation/Translator.php
new file mode 100644 (file)
index 0000000..8e74b79
--- /dev/null
@@ -0,0 +1,282 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+use Symfony\Component\Translation\Loader\LoaderInterface;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+
+/**
+ * Translator.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class Translator implements TranslatorInterface
+{
+    /**
+     * @var MessageCatalogueInterface[]
+     */
+    protected $catalogues = array();
+
+    /**
+     * @var string
+     */
+    protected $locale;
+
+    /**
+     * @var array
+     */
+    private $fallbackLocales = array();
+
+    /**
+     * @var LoaderInterface[]
+     */
+    private $loaders = array();
+
+    /**
+     * @var array
+     */
+    private $resources = array();
+
+    /**
+     * @var MessageSelector
+     */
+    private $selector;
+
+    /**
+     * Constructor.
+     *
+     * @param string               $locale   The locale
+     * @param MessageSelector|null $selector The message selector for pluralization
+     *
+     * @api
+     */
+    public function __construct($locale, MessageSelector $selector = null)
+    {
+        $this->locale = $locale;
+        $this->selector = $selector ?: new MessageSelector();
+    }
+
+    /**
+     * Adds a Loader.
+     *
+     * @param string          $format The name of the loader (@see addResource())
+     * @param LoaderInterface $loader A LoaderInterface instance
+     *
+     * @api
+     */
+    public function addLoader($format, LoaderInterface $loader)
+    {
+        $this->loaders[$format] = $loader;
+    }
+
+    /**
+     * Adds a Resource.
+     *
+     * @param string $format   The name of the loader (@see addLoader())
+     * @param mixed  $resource The resource name
+     * @param string $locale   The locale
+     * @param string $domain   The domain
+     *
+     * @api
+     */
+    public function addResource($format, $resource, $locale, $domain = null)
+    {
+        if (null === $domain) {
+            $domain = 'messages';
+        }
+
+        $this->resources[$locale][] = array($format, $resource, $domain);
+
+        if (in_array($locale, $this->fallbackLocales)) {
+            $this->catalogues = array();
+        } else {
+            unset($this->catalogues[$locale]);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function setLocale($locale)
+    {
+        $this->locale = $locale;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function getLocale()
+    {
+        return $this->locale;
+    }
+
+    /**
+     * Sets the fallback locale(s).
+     *
+     * @param string|array $locales The fallback locale(s)
+     *
+     * @deprecated since 2.3, to be removed in 3.0. Use setFallbackLocales() instead.
+     *
+     * @api
+     */
+    public function setFallbackLocale($locales)
+    {
+        $this->setFallbackLocales(is_array($locales) ? $locales : array($locales));
+    }
+
+    /**
+     * Sets the fallback locales.
+     *
+     * @param array $locales The fallback locales
+     *
+     * @api
+     */
+    public function setFallbackLocales(array $locales)
+    {
+        // needed as the fallback locales are linked to the already loaded catalogues
+        $this->catalogues = array();
+
+        $this->fallbackLocales = $locales;
+    }
+
+    /**
+     * Gets the fallback locales.
+     *
+     * @return array $locales The fallback locales
+     *
+     * @api
+     */
+    public function getFallbackLocales()
+    {
+        return $this->fallbackLocales;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function trans($id, array $parameters = array(), $domain = null, $locale = null)
+    {
+        if (null === $locale) {
+            $locale = $this->getLocale();
+        }
+
+        if (null === $domain) {
+            $domain = 'messages';
+        }
+
+        if (!isset($this->catalogues[$locale])) {
+            $this->loadCatalogue($locale);
+        }
+
+        return strtr($this->catalogues[$locale]->get((string) $id, $domain), $parameters);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null)
+    {
+        if (null === $locale) {
+            $locale = $this->getLocale();
+        }
+
+        if (null === $domain) {
+            $domain = 'messages';
+        }
+
+        if (!isset($this->catalogues[$locale])) {
+            $this->loadCatalogue($locale);
+        }
+
+        $id = (string) $id;
+
+        $catalogue = $this->catalogues[$locale];
+        while (!$catalogue->defines($id, $domain)) {
+            if ($cat = $catalogue->getFallbackCatalogue()) {
+                $catalogue = $cat;
+                $locale = $catalogue->getLocale();
+            } else {
+                break;
+            }
+        }
+
+        return strtr($this->selector->choose($catalogue->get($id, $domain), (int) $number, $locale), $parameters);
+    }
+
+    protected function loadCatalogue($locale)
+    {
+        try {
+            $this->doLoadCatalogue($locale);
+        } catch (NotFoundResourceException $e) {
+            if (!$this->computeFallbackLocales($locale)) {
+                throw $e;
+            }
+        }
+        $this->loadFallbackCatalogues($locale);
+    }
+
+    private function doLoadCatalogue($locale)
+    {
+        $this->catalogues[$locale] = new MessageCatalogue($locale);
+
+        if (isset($this->resources[$locale])) {
+            foreach ($this->resources[$locale] as $resource) {
+                if (!isset($this->loaders[$resource[0]])) {
+                    throw new \RuntimeException(sprintf('The "%s" translation loader is not registered.', $resource[0]));
+                }
+                $this->catalogues[$locale]->addCatalogue($this->loaders[$resource[0]]->load($resource[1], $locale, $resource[2]));
+            }
+        }
+    }
+
+    private function loadFallbackCatalogues($locale)
+    {
+        $current = $this->catalogues[$locale];
+
+        foreach ($this->computeFallbackLocales($locale) as $fallback) {
+            if (!isset($this->catalogues[$fallback])) {
+                $this->doLoadCatalogue($fallback);
+            }
+
+            $current->addFallbackCatalogue($this->catalogues[$fallback]);
+            $current = $this->catalogues[$fallback];
+        }
+    }
+
+    protected function computeFallbackLocales($locale)
+    {
+        $locales = array();
+        foreach ($this->fallbackLocales as $fallback) {
+            if ($fallback === $locale) {
+                continue;
+            }
+
+            $locales[] = $fallback;
+        }
+
+        if (strrchr($locale, '_') !== false) {
+            array_unshift($locales, substr($locale, 0, -strlen(strrchr($locale, '_'))));
+        }
+
+        return array_unique($locales);
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/TranslatorInterface.php b/vendor/symfony/translation/Symfony/Component/Translation/TranslatorInterface.php
new file mode 100644 (file)
index 0000000..3dcdd4f
--- /dev/null
@@ -0,0 +1,69 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation;
+
+/**
+ * TranslatorInterface.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+interface TranslatorInterface
+{
+    /**
+     * Translates the given message.
+     *
+     * @param string $id         The message id (may also be an object that can be cast to string)
+     * @param array  $parameters An array of parameters for the message
+     * @param string $domain     The domain for the message
+     * @param string $locale     The locale
+     *
+     * @return string The translated string
+     *
+     * @api
+     */
+    public function trans($id, array $parameters = array(), $domain = null, $locale = null);
+
+    /**
+     * Translates the given choice message by choosing a translation according to a number.
+     *
+     * @param string  $id         The message id (may also be an object that can be cast to string)
+     * @param integer $number     The number to use to find the indice of the message
+     * @param array   $parameters An array of parameters for the message
+     * @param string  $domain     The domain for the message
+     * @param string  $locale     The locale
+     *
+     * @return string The translated string
+     *
+     * @api
+     */
+    public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null);
+
+    /**
+     * Sets the current locale.
+     *
+     * @param string $locale The locale
+     *
+     * @api
+     */
+    public function setLocale($locale);
+
+    /**
+     * Returns the current locale.
+     *
+     * @return string The locale
+     *
+     * @api
+     */
+    public function getLocale();
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/Writer/TranslationWriter.php b/vendor/symfony/translation/Symfony/Component/Translation/Writer/TranslationWriter.php
new file mode 100644 (file)
index 0000000..9d70c12
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Writer;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Dumper\DumperInterface;
+
+/**
+ * TranslationWriter writes translation messages.
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+class TranslationWriter
+{
+    /**
+     * Dumpers used for export.
+     *
+     * @var array
+     */
+    private $dumpers = array();
+
+    /**
+     * Adds a dumper to the writer.
+     *
+     * @param string          $format The format of the dumper
+     * @param DumperInterface $dumper The dumper
+     */
+    public function addDumper($format, DumperInterface $dumper)
+    {
+        $this->dumpers[$format] = $dumper;
+    }
+
+    /**
+     * Obtains the list of supported formats.
+     *
+     * @return array
+     */
+    public function getFormats()
+    {
+        return array_keys($this->dumpers);
+    }
+
+    /**
+     * Writes translation from the catalogue according to the selected format.
+     *
+     * @param MessageCatalogue $catalogue The message catalogue to dump
+     * @param string           $format    The format to use to dump the messages
+     * @param array            $options   Options that are passed to the dumper
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function writeTranslations(MessageCatalogue $catalogue, $format, $options = array())
+    {
+        if (!isset($this->dumpers[$format])) {
+            throw new \InvalidArgumentException(sprintf('There is no dumper associated with format "%s".', $format));
+        }
+
+        // get the right dumper
+        $dumper = $this->dumpers[$format];
+
+        // save
+        $dumper->dump($catalogue, $options);
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/composer.json b/vendor/symfony/translation/Symfony/Component/Translation/composer.json
new file mode 100644 (file)
index 0000000..96a8e66
--- /dev/null
@@ -0,0 +1,39 @@
+{
+    "name": "symfony/translation",
+    "type": "library",
+    "description": "Symfony Translation 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/config": "~2.0",
+        "symfony/yaml": "~2.2"
+    },
+    "suggest": {
+        "symfony/config": "",
+        "symfony/yaml": ""
+    },
+    "autoload": {
+        "psr-0": { "Symfony\\Component\\Translation\\": "" }
+    },
+    "target-dir": "Symfony/Component/Translation",
+    "minimum-stability": "dev",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "2.3-dev"
+        }
+    }
+}
diff --git a/vendor/symfony/translation/Symfony/Component/Translation/phpunit.xml.dist b/vendor/symfony/translation/Symfony/Component/Translation/phpunit.xml.dist
new file mode 100644 (file)
index 0000000..1b37f21
--- /dev/null
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         processIsolation="false"
+         stopOnFailure="false"
+         syntaxCheck="false"
+         bootstrap="vendor/autoload.php"
+>
+    <testsuites>
+        <testsuite name="Symfony Translation Component Test Suite">
+            <directory>./Tests/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./</directory>
+            <exclude>
+                <directory>./vendor</directory>
+                <directory>./Tests</directory>
+            </exclude>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/.gitignore b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/.gitignore
new file mode 100644 (file)
index 0000000..44de97a
--- /dev/null
@@ -0,0 +1,4 @@
+vendor/
+composer.lock
+phpunit.xml
+
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/CHANGELOG.md b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/CHANGELOG.md
new file mode 100644 (file)
index 0000000..ad22216
--- /dev/null
@@ -0,0 +1,29 @@
+CHANGELOG
+=========
+
+2.3.0
+-----
+
+ * added helpers form(), form_start() and form_end()
+ * deprecated form_enctype() in favor of form_start()
+
+2.2.0
+-----
+
+ * added a `controller` function to help generating controller references
+ * added a `render_esi` and a `render_hinclude` function
+ * [BC BREAK] restricted the `render` tag to only accept URIs or ControllerReference instances (the signature changed)
+ * added a `render` function to render a request
+ * The `app` global variable is now injected even when using the twig service directly.
+ * Added an optional parameter to the `path` and `url` function which allows to generate
+   relative paths (e.g. "../parent-file") and scheme-relative URLs (e.g. "//example.com/dir/file").
+
+2.1.0
+-----
+
+ * added global variables access in a form theme
+ * added TwigEngine
+ * added TwigExtractor
+ * added a csrf_token function
+ * added a way to specify a default domain for a Twig template (via the
+   'trans_default_domain' tag)
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/CodeExtension.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/CodeExtension.php
new file mode 100644 (file)
index 0000000..a6202b2
--- /dev/null
@@ -0,0 +1,232 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Extension;
+
+if (!defined('ENT_SUBSTITUTE')) {
+    define('ENT_SUBSTITUTE', 8);
+}
+
+/**
+ * Twig extension relate to PHP code and used by the profiler and the default exception templates.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class CodeExtension extends \Twig_Extension
+{
+    private $fileLinkFormat;
+    private $rootDir;
+    private $charset;
+
+    /**
+     * Constructor.
+     *
+     * @param string $fileLinkFormat The format for links to source files
+     * @param string $rootDir        The project root directory
+     * @param string $charset        The charset
+     */
+    public function __construct($fileLinkFormat, $rootDir, $charset)
+    {
+        $this->fileLinkFormat = empty($fileLinkFormat) ? ini_get('xdebug.file_link_format') : $fileLinkFormat;
+        $this->rootDir = str_replace('\\', '/', $rootDir).'/';
+        $this->charset = $charset;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getFilters()
+    {
+        return array(
+            'abbr_class'            => new \Twig_Filter_Method($this, 'abbrClass', array('is_safe' => array('html'))),
+            'abbr_method'           => new \Twig_Filter_Method($this, 'abbrMethod', array('is_safe' => array('html'))),
+            'format_args'           => new \Twig_Filter_Method($this, 'formatArgs', array('is_safe' => array('html'))),
+            'format_args_as_text'   => new \Twig_Filter_Method($this, 'formatArgsAsText'),
+            'file_excerpt'          => new \Twig_Filter_Method($this, 'fileExcerpt', array('is_safe' => array('html'))),
+            'format_file'           => new \Twig_Filter_Method($this, 'formatFile', array('is_safe' => array('html'))),
+            'format_file_from_text' => new \Twig_Filter_Method($this, 'formatFileFromText', array('is_safe' => array('html'))),
+            'file_link'             => new \Twig_Filter_Method($this, 'getFileLink', array('is_safe' => array('html'))),
+        );
+    }
+
+    public function abbrClass($class)
+    {
+        $parts = explode('\\', $class);
+        $short = array_pop($parts);
+
+        return sprintf("<abbr title=\"%s\">%s</abbr>", $class, $short);
+    }
+
+    public function abbrMethod($method)
+    {
+        if (false !== strpos($method, '::')) {
+            list($class, $method) = explode('::', $method, 2);
+            $result = sprintf("%s::%s()", $this->abbrClass($class), $method);
+        } elseif ('Closure' === $method) {
+            $result = sprintf("<abbr title=\"%s\">%s</abbr>", $method, $method);
+        } else {
+            $result = sprintf("<abbr title=\"%s\">%s</abbr>()", $method, $method);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Formats an array as a string.
+     *
+     * @param array $args The argument array
+     *
+     * @return string
+     */
+    public function formatArgs($args)
+    {
+        $result = array();
+        foreach ($args as $key => $item) {
+            if ('object' === $item[0]) {
+                $parts = explode('\\', $item[1]);
+                $short = array_pop($parts);
+                $formattedValue = sprintf("<em>object</em>(<abbr title=\"%s\">%s</abbr>)", $item[1], $short);
+            } elseif ('array' === $item[0]) {
+                $formattedValue = sprintf("<em>array</em>(%s)", is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]);
+            } elseif ('string'  === $item[0]) {
+                $formattedValue = sprintf("'%s'", htmlspecialchars($item[1], ENT_QUOTES, $this->charset));
+            } elseif ('null' === $item[0]) {
+                $formattedValue = '<em>null</em>';
+            } elseif ('boolean' === $item[0]) {
+                $formattedValue = '<em>'.strtolower(var_export($item[1], true)).'</em>';
+            } elseif ('resource' === $item[0]) {
+                $formattedValue = '<em>resource</em>';
+            } else {
+                $formattedValue = str_replace("\n", '', var_export(htmlspecialchars((string) $item[1], ENT_QUOTES, $this->charset), true));
+            }
+
+            $result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue);
+        }
+
+        return implode(', ', $result);
+    }
+
+    /**
+     * Formats an array as a string.
+     *
+     * @param array $args The argument array
+     *
+     * @return string
+     */
+    public function formatArgsAsText($args)
+    {
+        return strip_tags($this->formatArgs($args));
+    }
+
+    /**
+     * Returns an excerpt of a code file around the given line number.
+     *
+     * @param string $file A file path
+     * @param int    $line The selected line number
+     *
+     * @return string An HTML string
+     */
+    public function fileExcerpt($file, $line)
+    {
+        if (is_readable($file)) {
+            $code = highlight_file($file, true);
+            // remove main code/span tags
+            $code = preg_replace('#^<code.*?>\s*<span.*?>(.*)</span>\s*</code>#s', '\\1', $code);
+            $content = preg_split('#<br />#', $code);
+
+            $lines = array();
+            for ($i = max($line - 3, 1), $max = min($line + 3, count($content)); $i <= $max; $i++) {
+                $lines[] = '<li'.($i == $line ? ' class="selected"' : '').'><code>'.self::fixCodeMarkup($content[$i - 1]).'</code></li>';
+            }
+
+            return '<ol start="'.max($line - 3, 1).'">'.implode("\n", $lines).'</ol>';
+        }
+    }
+
+    /**
+     * Formats a file path.
+     *
+     * @param string  $file An absolute file path
+     * @param integer $line The line number
+     * @param string  $text Use this text for the link rather than the file path
+     *
+     * @return string
+     */
+    public function formatFile($file, $line, $text = null)
+    {
+        if (null === $text) {
+            $file = trim($file);
+            $text = $file;
+            if (0 === strpos($text, $this->rootDir)) {
+                $text = str_replace($this->rootDir, '', str_replace('\\', '/', $text));
+                $text = sprintf('<abbr title="%s">kernel.root_dir</abbr>/%s', $this->rootDir, $text);
+            }
+        }
+
+        $text = "$text at line $line";
+
+        if (false !== $link = $this->getFileLink($file, $line)) {
+            return sprintf('<a href="%s" title="Click to open this file" class="file_link">%s</a>', htmlspecialchars($link, ENT_QUOTES | ENT_SUBSTITUTE, $this->charset), $text);
+        }
+
+        return $text;
+    }
+
+    /**
+     * Returns the link for a given file/line pair.
+     *
+     * @param string  $file An absolute file path
+     * @param integer $line The line number
+     *
+     * @return string A link of false
+     */
+    public function getFileLink($file, $line)
+    {
+        if ($this->fileLinkFormat && is_file($file)) {
+            return strtr($this->fileLinkFormat, array('%f' => $file, '%l' => $line));
+        }
+
+        return false;
+    }
+
+    public function formatFileFromText($text)
+    {
+        $that = $this;
+
+        return preg_replace_callback('/in ("|&quot;)?(.+?)\1(?: +(?:on|at))? +line (\d+)/s', function ($match) use ($that) {
+            return 'in '.$that->formatFile($match[2], $match[3]);
+        }, $text);
+    }
+
+    public function getName()
+    {
+        return 'code';
+    }
+
+    protected static function fixCodeMarkup($line)
+    {
+        // </span> ending tag from previous line
+        $opening = strpos($line, '<span');
+        $closing = strpos($line, '</span>');
+        if (false !== $closing && (false === $opening || $closing < $opening)) {
+            $line = substr_replace($line, '', $closing, 7);
+        }
+
+        // missing </span> tag at the end of line
+        $opening = strpos($line, '<span');
+        $closing = strpos($line, '</span>');
+        if (false !== $opening && (false === $closing || $closing > $opening)) {
+            $line .= '</span>';
+        }
+
+        return $line;
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/FormExtension.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/FormExtension.php
new file mode 100644 (file)
index 0000000..fbfa524
--- /dev/null
@@ -0,0 +1,136 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Extension;
+
+use Symfony\Bridge\Twig\TokenParser\FormThemeTokenParser;
+use Symfony\Bridge\Twig\Form\TwigRendererInterface;
+use Symfony\Component\Form\Extension\Core\View\ChoiceView;
+
+/**
+ * FormExtension extends Twig with form capabilities.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class FormExtension extends \Twig_Extension
+{
+    /**
+     * This property is public so that it can be accessed directly from compiled
+     * templates without having to call a getter, which slightly decreases performance.
+     *
+     * @var TwigRendererInterface
+     */
+    public $renderer;
+
+    public function __construct(TwigRendererInterface $renderer)
+    {
+        $this->renderer = $renderer;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function initRuntime(\Twig_Environment $environment)
+    {
+        $this->renderer->setEnvironment($environment);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getTokenParsers()
+    {
+        return array(
+            // {% form_theme form "SomeBundle::widgets.twig" %}
+            new FormThemeTokenParser(),
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getFunctions()
+    {
+        return array(
+            'form_enctype' => new \Twig_Function_Node('Symfony\Bridge\Twig\Node\FormEnctypeNode', array('is_safe' => array('html'))),
+            'form_widget'  => new \Twig_Function_Node('Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', array('is_safe' => array('html'))),
+            'form_errors'  => new \Twig_Function_Node('Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', array('is_safe' => array('html'))),
+            'form_label'   => new \Twig_Function_Node('Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', array('is_safe' => array('html'))),
+            'form_row'     => new \Twig_Function_Node('Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', array('is_safe' => array('html'))),
+            'form_rest'    => new \Twig_Function_Node('Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', array('is_safe' => array('html'))),
+            'form'         => new \Twig_Function_Node('Symfony\Bridge\Twig\Node\RenderBlockNode', array('is_safe' => array('html'))),
+            'form_start'   => new \Twig_Function_Node('Symfony\Bridge\Twig\Node\RenderBlockNode', array('is_safe' => array('html'))),
+            'form_end'     => new \Twig_Function_Node('Symfony\Bridge\Twig\Node\RenderBlockNode', array('is_safe' => array('html'))),
+            'csrf_token'   => new \Twig_Function_Method($this, 'renderer->renderCsrfToken'),
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getFilters()
+    {
+        return array(
+            'humanize' => new \Twig_Filter_Method($this, 'renderer->humanize'),
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getTests()
+    {
+        return array(
+            'selectedchoice' => new \Twig_Test_Method($this, 'isSelectedChoice'),
+        );
+    }
+
+    /**
+     * Returns whether a choice is selected for a given form value.
+     *
+     * Unfortunately Twig does not support an efficient way to execute the
+     * "is_selected" closure passed to the template by ChoiceType. It is faster
+     * to implement the logic here (around 65ms for a specific form).
+     *
+     * Directly implementing the logic here is also faster than doing so in
+     * ChoiceView (around 30ms).
+     *
+     * The worst option tested so far is to implement the logic in ChoiceView
+     * and access the ChoiceView method directly in the template. Doing so is
+     * around 220ms slower than doing the method call here in the filter. Twig
+     * seems to be much more efficient at executing filters than at executing
+     * methods of an object.
+     *
+     * @param ChoiceView   $choice        The choice to check.
+     * @param string|array $selectedValue The selected value to compare.
+     *
+     * @return Boolean Whether the choice is selected.
+     *
+     * @see ChoiceView::isSelected()
+     */
+    public function isSelectedChoice(ChoiceView $choice, $selectedValue)
+    {
+        if (is_array($selectedValue)) {
+            return false !== array_search($choice->value, $selectedValue, true);
+        }
+
+        return $choice->value === $selectedValue;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getName()
+    {
+        return 'form';
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/HttpKernelExtension.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/HttpKernelExtension.php
new file mode 100644 (file)
index 0000000..a837723
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Extension;
+
+use Symfony\Component\HttpKernel\Fragment\FragmentHandler;
+use Symfony\Component\HttpKernel\Controller\ControllerReference;
+
+/**
+ * Provides integration with the HttpKernel component.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class HttpKernelExtension extends \Twig_Extension
+{
+    private $handler;
+
+    /**
+     * Constructor.
+     *
+     * @param FragmentHandler $handler A FragmentHandler instance
+     */
+    public function __construct(FragmentHandler $handler)
+    {
+        $this->handler = $handler;
+    }
+
+    public function getFunctions()
+    {
+        return array(
+            'render'     => new \Twig_Function_Method($this, 'renderFragment', array('is_safe' => array('html'))),
+            'render_*'   => new \Twig_Function_Method($this, 'renderFragmentStrategy', array('is_safe' => array('html'))),
+            'controller' => new \Twig_Function_Method($this, 'controller'),
+        );
+    }
+
+    /**
+     * Renders a fragment.
+     *
+     * @param string|ControllerReference $uri      A URI as a string or a ControllerReference instance
+     * @param array                      $options  An array of options
+     *
+     * @return string The fragment content
+     *
+     * @see Symfony\Component\HttpKernel\Fragment\FragmentHandler::render()
+     */
+    public function renderFragment($uri, $options = array())
+    {
+        $strategy = isset($options['strategy']) ? $options['strategy'] : 'inline';
+        unset($options['strategy']);
+
+        return $this->handler->render($uri, $strategy, $options);
+    }
+
+    /**
+     * Renders a fragment.
+     *
+     * @param string                     $strategy A strategy name
+     * @param string|ControllerReference $uri      A URI as a string or a ControllerReference instance
+     * @param array                      $options  An array of options
+     *
+     * @return string The fragment content
+     *
+     * @see Symfony\Component\HttpKernel\Fragment\FragmentHandler::render()
+     */
+    public function renderFragmentStrategy($strategy, $uri, $options = array())
+    {
+        return $this->handler->render($uri, $strategy, $options);
+    }
+
+    public function controller($controller, $attributes = array(), $query = array())
+    {
+        return new ControllerReference($controller, $attributes, $query);
+    }
+
+    public function getName()
+    {
+        return 'http_kernel';
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/RoutingExtension.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/RoutingExtension.php
new file mode 100644 (file)
index 0000000..8274abf
--- /dev/null
@@ -0,0 +1,100 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Extension;
+
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
+
+/**
+ * Provides integration of the Routing component with Twig.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class RoutingExtension extends \Twig_Extension
+{
+    private $generator;
+
+    public function __construct(UrlGeneratorInterface $generator)
+    {
+        $this->generator = $generator;
+    }
+
+    /**
+     * Returns a list of functions to add to the existing list.
+     *
+     * @return array An array of functions
+     */
+    public function getFunctions()
+    {
+        return array(
+            'url'  => new \Twig_Function_Method($this, 'getUrl', array('is_safe_callback' => array($this, 'isUrlGenerationSafe'))),
+            'path' => new \Twig_Function_Method($this, 'getPath', array('is_safe_callback' => array($this, 'isUrlGenerationSafe'))),
+        );
+    }
+
+    public function getPath($name, $parameters = array(), $relative = false)
+    {
+        return $this->generator->generate($name, $parameters, $relative ? UrlGeneratorInterface::RELATIVE_PATH : UrlGeneratorInterface::ABSOLUTE_PATH);
+    }
+
+    public function getUrl($name, $parameters = array(), $schemeRelative = false)
+    {
+        return $this->generator->generate($name, $parameters, $schemeRelative ? UrlGeneratorInterface::NETWORK_PATH : UrlGeneratorInterface::ABSOLUTE_URL);
+    }
+
+    /**
+     * Determines at compile time whether the generated URL will be safe and thus
+     * saving the unneeded automatic escaping for performance reasons.
+     *
+     * The URL generation process percent encodes non-alphanumeric characters. So there is no risk
+     * that malicious/invalid characters are part of the URL. The only character within an URL that
+     * must be escaped in html is the ampersand ("&") which separates query params. So we cannot mark
+     * the URL generation as always safe, but only when we are sure there won't be multiple query
+     * params. This is the case when there are none or only one constant parameter given.
+     * E.g. we know beforehand this will be safe:
+     * - path('route')
+     * - path('route', {'param': 'value'})
+     * But the following may not:
+     * - path('route', var)
+     * - path('route', {'param': ['val1', 'val2'] }) // a sub-array
+     * - path('route', {'param1': 'value1', 'param2': 'value2'})
+     * If param1 and param2 reference placeholder in the route, it would still be safe. But we don't know.
+     *
+     * @param \Twig_Node $argsNode The arguments of the path/url function
+     *
+     * @return array An array with the contexts the URL is safe
+     */
+    public function isUrlGenerationSafe(\Twig_Node $argsNode)
+    {
+        // support named arguments
+        $paramsNode = $argsNode->hasNode('parameters') ? $argsNode->getNode('parameters') : (
+            $argsNode->hasNode(1) ? $argsNode->getNode(1) : null
+        );
+
+        if (null === $paramsNode || $paramsNode instanceof \Twig_Node_Expression_Array && count($paramsNode) <= 2 &&
+            (!$paramsNode->hasNode(1) || $paramsNode->getNode(1) instanceof \Twig_Node_Expression_Constant)
+        ) {
+            return array('html');
+        }
+
+        return array();
+    }
+
+    /**
+     * Returns the name of the extension.
+     *
+     * @return string The extension name
+     */
+    public function getName()
+    {
+        return 'routing';
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/SecurityExtension.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/SecurityExtension.php
new file mode 100644 (file)
index 0000000..c9f4466
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Extension;
+
+use Symfony\Component\Security\Acl\Voter\FieldVote;
+use Symfony\Component\Security\Core\SecurityContextInterface;
+
+/**
+ * SecurityExtension exposes security context features.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class SecurityExtension extends \Twig_Extension
+{
+    private $context;
+
+    public function __construct(SecurityContextInterface $context = null)
+    {
+        $this->context = $context;
+    }
+
+    public function isGranted($role, $object = null, $field = null)
+    {
+        if (null === $this->context) {
+            return false;
+        }
+
+        if (null !== $field) {
+            $object = new FieldVote($object, $field);
+        }
+
+        return $this->context->isGranted($role, $object);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getFunctions()
+    {
+        return array(
+            'is_granted' => new \Twig_Function_Method($this, 'isGranted'),
+        );
+    }
+
+    /**
+     * Returns the name of the extension.
+     *
+     * @return string The extension name
+     */
+    public function getName()
+    {
+        return 'security';
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/TranslationExtension.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/TranslationExtension.php
new file mode 100644 (file)
index 0000000..2ab3832
--- /dev/null
@@ -0,0 +1,118 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Extension;
+
+use Symfony\Bridge\Twig\TokenParser\TransTokenParser;
+use Symfony\Bridge\Twig\TokenParser\TransChoiceTokenParser;
+use Symfony\Bridge\Twig\TokenParser\TransDefaultDomainTokenParser;
+use Symfony\Component\Translation\TranslatorInterface;
+use Symfony\Bridge\Twig\NodeVisitor\TranslationNodeVisitor;
+use Symfony\Bridge\Twig\NodeVisitor\TranslationDefaultDomainNodeVisitor;
+
+/**
+ * Provides integration of the Translation component with Twig.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class TranslationExtension extends \Twig_Extension
+{
+    private $translator;
+    private $translationNodeVisitor;
+
+    public function __construct(TranslatorInterface $translator, \Twig_NodeVisitorInterface $translationNodeVisitor = null)
+    {
+        if (!$translationNodeVisitor) {
+            $translationNodeVisitor = new TranslationNodeVisitor();
+        }
+
+        $this->translator = $translator;
+        $this->translationNodeVisitor = $translationNodeVisitor;
+    }
+
+    public function getTranslator()
+    {
+        return $this->translator;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getFilters()
+    {
+        return array(
+            'trans' => new \Twig_Filter_Method($this, 'trans'),
+            'transchoice' => new \Twig_Filter_Method($this, 'transchoice'),
+        );
+    }
+
+    /**
+     * Returns the token parser instance to add to the existing list.
+     *
+     * @return array An array of Twig_TokenParser instances
+     */
+    public function getTokenParsers()
+    {
+        return array(
+            // {% trans %}Symfony is great!{% endtrans %}
+            new TransTokenParser(),
+
+            // {% transchoice count %}
+            //     {0} There is no apples|{1} There is one apple|]1,Inf] There is {{ count }} apples
+            // {% endtranschoice %}
+            new TransChoiceTokenParser(),
+
+            // {% trans_default_domain "foobar" %}
+            new TransDefaultDomainTokenParser(),
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getNodeVisitors()
+    {
+        return array($this->translationNodeVisitor, new TranslationDefaultDomainNodeVisitor());
+    }
+
+    public function getTranslationNodeVisitor()
+    {
+        return $this->translationNodeVisitor;
+    }
+
+    public function trans($message, array $arguments = array(), $domain = null, $locale = null)
+    {
+        if (null === $domain) {
+            $domain = 'messages';
+        }
+
+        return $this->translator->trans($message, $arguments, $domain, $locale);
+    }
+
+    public function transchoice($message, $count, array $arguments = array(), $domain = null, $locale = null)
+    {
+        if (null === $domain) {
+            $domain = 'messages';
+        }
+
+        return $this->translator->transChoice($message, $count, array_merge(array('%count%' => $count), $arguments), $domain, $locale);
+    }
+
+    /**
+     * Returns the name of the extension.
+     *
+     * @return string The extension name
+     */
+    public function getName()
+    {
+        return 'translator';
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/YamlExtension.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/YamlExtension.php
new file mode 100644 (file)
index 0000000..f88829e
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Extension;
+
+use Symfony\Component\Yaml\Dumper as YamlDumper;
+
+/**
+ * Provides integration of the Yaml component with Twig.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class YamlExtension extends \Twig_Extension
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getFilters()
+    {
+        return array(
+            'yaml_encode' => new \Twig_Filter_Method($this, 'encode'),
+            'yaml_dump'   => new \Twig_Filter_Method($this, 'dump'),
+        );
+    }
+
+    public function encode($input, $inline = 0, $dumpObjects = false)
+    {
+        static $dumper;
+
+        if (null === $dumper) {
+            $dumper = new YamlDumper();
+        }
+
+        return $dumper->dump($input, $inline, false, $dumpObjects);
+    }
+
+    public function dump($value, $inline = 0, $dumpObjects = false)
+    {
+        if (is_resource($value)) {
+            return '%Resource%';
+        }
+
+        if (is_array($value) || is_object($value)) {
+            return '%'.gettype($value).'% '.$this->encode($value, $inline, $dumpObjects);
+        }
+
+        return $this->encode($value, $inline, $dumpObjects);
+    }
+
+    /**
+     * Returns the name of the extension.
+     *
+     * @return string The extension name
+     */
+    public function getName()
+    {
+        return 'yaml';
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Form/TwigRenderer.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Form/TwigRenderer.php
new file mode 100644 (file)
index 0000000..72798d1
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Form;
+
+use Symfony\Component\Form\FormRenderer;
+use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class TwigRenderer extends FormRenderer implements TwigRendererInterface
+{
+    /**
+     * @var TwigRendererEngineInterface
+     */
+    private $engine;
+
+    public function __construct(TwigRendererEngineInterface $engine, CsrfProviderInterface $csrfProvider = null)
+    {
+        parent::__construct($engine, $csrfProvider);
+
+        $this->engine = $engine;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setEnvironment(\Twig_Environment $environment)
+    {
+        $this->engine->setEnvironment($environment);
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Form/TwigRendererEngine.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Form/TwigRendererEngine.php
new file mode 100644 (file)
index 0000000..05beb31
--- /dev/null
@@ -0,0 +1,183 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Form;
+
+use Symfony\Component\Form\AbstractRendererEngine;
+use Symfony\Component\Form\FormView;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class TwigRendererEngine extends AbstractRendererEngine implements TwigRendererEngineInterface
+{
+    /**
+     * @var \Twig_Environment
+     */
+    private $environment;
+
+    /**
+     * @var \Twig_Template
+     */
+    private $template;
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setEnvironment(\Twig_Environment $environment)
+    {
+        $this->environment = $environment;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function renderBlock(FormView $view, $resource, $blockName, array $variables = array())
+    {
+        $cacheKey = $view->vars[self::CACHE_KEY_VAR];
+
+        $context = $this->environment->mergeGlobals($variables);
+
+        ob_start();
+
+        // By contract,This method can only be called after getting the resource
+        // (which is passed to the method). Getting a resource for the first time
+        // (with an empty cache) is guaranteed to invoke loadResourcesFromTheme(),
+        // where the property $template is initialized.
+
+        // We do not call renderBlock here to avoid too many nested level calls
+        // (XDebug limits the level to 100 by default)
+        $this->template->displayBlock($blockName, $context, $this->resources[$cacheKey]);
+
+        return ob_get_clean();
+    }
+
+    /**
+     * Loads the cache with the resource for a given block name.
+     *
+     * This implementation eagerly loads all blocks of the themes assigned to the given view
+     * and all of its ancestors views. This is necessary, because Twig receives the
+     * list of blocks later. At that point, all blocks must already be loaded, for the
+     * case that the function "block()" is used in the Twig template.
+     *
+     * @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)
+    {
+        // The caller guarantees that $this->resources[$cacheKey][$block] is
+        // not set, but it doesn't have to check whether $this->resources[$cacheKey]
+        // is set. If $this->resources[$cacheKey] is set, all themes for this
+        // $cacheKey are already loaded (due to the eager population, see doc comment).
+        if (isset($this->resources[$cacheKey])) {
+            // As said in the previous, the caller guarantees that
+            // $this->resources[$cacheKey][$block] is not set. Since the themes are
+            // already loaded, it can only be a non-existing block.
+            $this->resources[$cacheKey][$blockName] = false;
+
+            return false;
+        }
+
+        // Recursively try to find the block in the themes assigned to $view,
+        // then of its parent view, then of the parent view of the parent and so on.
+        // When the root view 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) {
+                $this->loadResourcesFromTheme($cacheKey, $this->themes[$cacheKey][$i]);
+                // CONTINUE LOADING (see doc comment)
+            }
+        }
+
+        // Check the default themes once we reach the root view without success
+        if (!$view->parent) {
+            for ($i = count($this->defaultThemes) - 1; $i >= 0; --$i) {
+                $this->loadResourcesFromTheme($cacheKey, $this->defaultThemes[$i]);
+                // CONTINUE LOADING (see doc comment)
+            }
+        }
+
+        // Proceed with the themes of the parent view
+        if ($view->parent) {
+            $parentCacheKey = $view->parent->vars[self::CACHE_KEY_VAR];
+
+            if (!isset($this->resources[$parentCacheKey])) {
+                $this->loadResourceForBlockName($parentCacheKey, $view->parent, $blockName);
+            }
+
+            // EAGER CACHE POPULATION (see doc comment)
+            foreach ($this->resources[$parentCacheKey] as $nestedBlockName => $resource) {
+                if (!isset($this->resources[$cacheKey][$nestedBlockName])) {
+                    $this->resources[$cacheKey][$nestedBlockName] = $resource;
+                }
+            }
+        }
+
+        // Even though we loaded the themes, it can happen that none of them
+        // contains the searched block
+        if (!isset($this->resources[$cacheKey][$blockName])) {
+            // Cache that we didn't find anything to speed up further accesses
+            $this->resources[$cacheKey][$blockName] = false;
+        }
+
+        return false !== $this->resources[$cacheKey][$blockName];
+    }
+
+    /**
+     * Loads the resources for all blocks in a theme.
+     *
+     * @param string $cacheKey The cache key for storing the resource.
+     * @param mixed  $theme    The theme to load the block from. This parameter
+     *                         is passed by reference, because it might be necessary
+     *                         to initialize the theme first. Any changes made to
+     *                         this variable will be kept and be available upon
+     *                         further calls to this method using the same theme.
+     */
+    protected function loadResourcesFromTheme($cacheKey, &$theme)
+    {
+        if (!$theme instanceof \Twig_Template) {
+            /* @var \Twig_Template $theme */
+            $theme = $this->environment->loadTemplate($theme);
+        }
+
+        if (null === $this->template) {
+            // Store the first \Twig_Template instance that we find so that
+            // we can call displayBlock() later on. It doesn't matter *which*
+            // template we use for that, since we pass the used blocks manually
+            // anyway.
+            $this->template = $theme;
+        }
+
+        // Use a separate variable for the inheritance traversal, because
+        // theme is a reference and we don't want to change it.
+        $currentTheme = $theme;
+
+        // The do loop takes care of template inheritance.
+        // Add blocks from all templates in the inheritance tree, but avoid
+        // overriding blocks already set.
+        do {
+            foreach ($currentTheme->getBlocks() as $block => $blockData) {
+                if (!isset($this->resources[$cacheKey][$block])) {
+                    // The resource given back is the key to the bucket that
+                    // contains this block.
+                    $this->resources[$cacheKey][$block] = $blockData;
+                }
+            }
+        } while (false !== $currentTheme = $currentTheme->getParent(array()));
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Form/TwigRendererEngineInterface.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Form/TwigRendererEngineInterface.php
new file mode 100644 (file)
index 0000000..ef764a2
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Form;
+
+use Symfony\Component\Form\FormRendererEngineInterface;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+interface TwigRendererEngineInterface extends FormRendererEngineInterface
+{
+    /**
+     * Sets Twig's environment.
+     *
+     * @param \Twig_Environment $environment
+     */
+    public function setEnvironment(\Twig_Environment $environment);
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Form/TwigRendererInterface.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Form/TwigRendererInterface.php
new file mode 100644 (file)
index 0000000..4682f52
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Form;
+
+use Symfony\Component\Form\FormRendererInterface;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+interface TwigRendererInterface extends FormRendererInterface
+{
+    /**
+     * Sets Twig's environment.
+     *
+     * @param \Twig_Environment $environment
+     */
+    public function setEnvironment(\Twig_Environment $environment);
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/LICENSE b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/LICENSE
new file mode 100644 (file)
index 0000000..88a57f8
--- /dev/null
@@ -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/twig-bridge/Symfony/Bridge/Twig/Node/FormEnctypeNode.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/FormEnctypeNode.php
new file mode 100644 (file)
index 0000000..93bce1b
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Node;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ *
+ * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use
+ *             the helper "form_start()" instead.
+ */
+class FormEnctypeNode extends SearchAndRenderBlockNode
+{
+    public function compile(\Twig_Compiler $compiler)
+    {
+        parent::compile($compiler);
+
+        $compiler->raw(";\n");
+
+        // Uncomment this as soon as the deprecation note should be shown
+        // $compiler->write('trigger_error(\'The helper form_enctype(form) is deprecated since version 2.3 and will be removed in 3.0. Use form_start(form) instead.\', E_USER_DEPRECATED)');
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/FormThemeNode.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/FormThemeNode.php
new file mode 100644 (file)
index 0000000..329ab86
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Node;
+
+/**
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class FormThemeNode extends \Twig_Node
+{
+    public function __construct(\Twig_NodeInterface $form, \Twig_NodeInterface $resources, $lineno, $tag = null)
+    {
+        parent::__construct(array('form' => $form, 'resources' => $resources), array(), $lineno, $tag);
+    }
+
+    /**
+     * Compiles the node to PHP.
+     *
+     * @param \Twig_Compiler $compiler A Twig_Compiler instance
+     */
+    public function compile(\Twig_Compiler $compiler)
+    {
+        $compiler
+            ->addDebugInfo($this)
+            ->write('$this->env->getExtension(\'form\')->renderer->setTheme(')
+            ->subcompile($this->getNode('form'))
+            ->raw(', ')
+            ->subcompile($this->getNode('resources'))
+            ->raw(");\n");
+        ;
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/RenderBlockNode.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/RenderBlockNode.php
new file mode 100644 (file)
index 0000000..822a272
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Node;
+
+/**
+ * Compiles a call to {@link FormRendererInterface::renderBlock()}.
+ *
+ * The function name is used as block name. For example, if the function name
+ * is "foo", the block "foo" will be rendered.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class RenderBlockNode extends \Twig_Node_Expression_Function
+{
+    public function compile(\Twig_Compiler $compiler)
+    {
+        $compiler->addDebugInfo($this);
+        $arguments = iterator_to_array($this->getNode('arguments'));
+        $compiler->write('$this->env->getExtension(\'form\')->renderer->renderBlock(');
+
+        if (isset($arguments[0])) {
+            $compiler->subcompile($arguments[0]);
+            $compiler->raw(', \'' . $this->getAttribute('name') . '\'');
+
+            if (isset($arguments[1])) {
+                $compiler->raw(', ');
+                $compiler->subcompile($arguments[1]);
+            }
+        }
+
+        $compiler->raw(')');
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/SearchAndRenderBlockNode.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/SearchAndRenderBlockNode.php
new file mode 100644 (file)
index 0000000..630e263
--- /dev/null
@@ -0,0 +1,106 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Node;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class SearchAndRenderBlockNode extends \Twig_Node_Expression_Function
+{
+    public function compile(\Twig_Compiler $compiler)
+    {
+        $compiler->addDebugInfo($this);
+        $compiler->raw('$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(');
+
+        preg_match('/_([^_]+)$/', $this->getAttribute('name'), $matches);
+
+        $label = null;
+        $arguments = iterator_to_array($this->getNode('arguments'));
+        $blockNameSuffix = $matches[1];
+
+        if (isset($arguments[0])) {
+            $compiler->subcompile($arguments[0]);
+            $compiler->raw(', \''.$blockNameSuffix.'\'');
+
+            if (isset($arguments[1])) {
+                if ('label' === $blockNameSuffix) {
+                    // The "label" function expects the label in the second and
+                    // the variables in the third argument
+                    $label = $arguments[1];
+                    $variables = isset($arguments[2]) ? $arguments[2] : null;
+                    $lineno = $label->getLine();
+
+                    if ($label instanceof \Twig_Node_Expression_Constant) {
+                        // If the label argument is given as a constant, we can either
+                        // strip it away if it is empty, or integrate it into the array
+                        // of variables at compile time.
+                        $labelIsExpression = false;
+
+                        // Only insert the label into the array if it is not empty
+                        if (!twig_test_empty($label->getAttribute('value'))) {
+                            $originalVariables = $variables;
+                            $variables = new \Twig_Node_Expression_Array(array(), $lineno);
+                            $labelKey = new \Twig_Node_Expression_Constant('label', $lineno);
+
+                            if (null !== $originalVariables) {
+                                foreach ($originalVariables->getKeyValuePairs() as $pair) {
+                                    // Don't copy the original label attribute over if it exists
+                                    if ((string) $labelKey !== (string) $pair['key']) {
+                                        $variables->addElement($pair['value'], $pair['key']);
+                                    }
+                                }
+                            }
+
+                            // Insert the label argument into the array
+                            $variables->addElement($label, $labelKey);
+                        }
+                    } else {
+                        // The label argument is not a constant, but some kind of
+                        // expression. This expression needs to be evaluated at runtime.
+                        // Depending on the result (whether it is null or not), the
+                        // label in the arguments should take precedence over the label
+                        // in the attributes or not.
+                        $labelIsExpression = true;
+                    }
+                } else {
+                    // All other functions than "label" expect the variables
+                    // in the second argument
+                    $label = null;
+                    $variables = $arguments[1];
+                    $labelIsExpression = false;
+                }
+
+                if (null !== $variables || $labelIsExpression) {
+                    $compiler->raw(', ');
+
+                    if (null !== $variables) {
+                        $compiler->subcompile($variables);
+                    }
+
+                    if ($labelIsExpression) {
+                        if (null !== $variables) {
+                            $compiler->raw(' + ');
+                        }
+
+                        // Check at runtime whether the label is empty.
+                        // If not, add it to the array at runtime.
+                        $compiler->raw('(twig_test_empty($_label_ = ');
+                        $compiler->subcompile($label);
+                        $compiler->raw(') ? array() : array("label" => $_label_))');
+                    }
+                }
+            }
+        }
+
+        $compiler->raw(")");
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/TransDefaultDomainNode.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/TransDefaultDomainNode.php
new file mode 100644 (file)
index 0000000..adee71f
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Node;
+
+/**
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class TransDefaultDomainNode extends \Twig_Node
+{
+    public function __construct(\Twig_Node_Expression $expr, $lineno = 0, $tag = null)
+    {
+        parent::__construct(array('expr' => $expr), array(), $lineno, $tag);
+    }
+
+    /**
+     * Compiles the node to PHP.
+     *
+     * @param \Twig_Compiler $compiler A Twig_Compiler instance
+     */
+    public function compile(\Twig_Compiler $compiler)
+    {
+        // noop as this node is just a marker for TranslationDefaultDomainNodeVisitor
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/TransNode.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/TransNode.php
new file mode 100644 (file)
index 0000000..a68c101
--- /dev/null
@@ -0,0 +1,119 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Node;
+
+/**
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class TransNode extends \Twig_Node
+{
+    public function __construct(\Twig_NodeInterface $body, \Twig_NodeInterface $domain = null, \Twig_Node_Expression $count = null, \Twig_Node_Expression $vars = null, \Twig_Node_Expression $locale = null, $lineno = 0, $tag = null)
+    {
+        parent::__construct(array('count' => $count, 'body' => $body, 'domain' => $domain, 'vars' => $vars, 'locale' => $locale), array(), $lineno, $tag);
+    }
+
+    /**
+     * Compiles the node to PHP.
+     *
+     * @param \Twig_Compiler $compiler A Twig_Compiler instance
+     */
+    public function compile(\Twig_Compiler $compiler)
+    {
+        $compiler->addDebugInfo($this);
+
+        $vars = $this->getNode('vars');
+        $defaults = new \Twig_Node_Expression_Array(array(), -1);
+        if ($vars instanceof \Twig_Node_Expression_Array) {
+            $defaults = $this->getNode('vars');
+            $vars = null;
+        }
+        list($msg, $defaults) = $this->compileString($this->getNode('body'), $defaults);
+
+        $method = null === $this->getNode('count') ? 'trans' : 'transChoice';
+
+        $compiler
+            ->write('echo $this->env->getExtension(\'translator\')->getTranslator()->'.$method.'(')
+            ->subcompile($msg)
+        ;
+
+        $compiler->raw(', ');
+
+        if (null !== $this->getNode('count')) {
+            $compiler
+                ->subcompile($this->getNode('count'))
+                ->raw(', ')
+            ;
+        }
+
+        if (null !== $vars) {
+            $compiler
+                ->raw('array_merge(')
+                ->subcompile($defaults)
+                ->raw(', ')
+                ->subcompile($this->getNode('vars'))
+                ->raw(')')
+            ;
+        } else {
+            $compiler->subcompile($defaults);
+        }
+
+        $compiler->raw(', ');
+
+        if (null === $this->getNode('domain')) {
+            $compiler->repr('messages');
+        } else {
+            $compiler->subcompile($this->getNode('domain'));
+        }
+
+        if (null !== $this->getNode('locale')) {
+            $compiler
+                ->raw(', ')
+                ->subcompile($this->getNode('locale'))
+            ;
+        }
+        $compiler->raw(");\n");
+    }
+
+    protected function compileString(\Twig_NodeInterface $body, \Twig_Node_Expression_Array $vars)
+    {
+        if ($body instanceof \Twig_Node_Expression_Constant) {
+            $msg = $body->getAttribute('value');
+        } elseif ($body instanceof \Twig_Node_Text) {
+            $msg = $body->getAttribute('data');
+        } else {
+            return array($body, $vars);
+        }
+
+        preg_match_all('/(?<!%)%([^%]+)%/', $msg, $matches);
+
+        if (version_compare(\Twig_Environment::VERSION, '1.5', '>=')) {
+            foreach ($matches[1] as $var) {
+                $key = new \Twig_Node_Expression_Constant('%'.$var.'%', $body->getLine());
+                if (!$vars->hasElement($key)) {
+                    $vars->addElement(new \Twig_Node_Expression_Name($var, $body->getLine()), $key);
+                }
+            }
+        } else {
+            $current = array();
+            foreach ($vars as $name => $var) {
+                $current[$name] = true;
+            }
+            foreach ($matches[1] as $var) {
+                if (!isset($current['%'.$var.'%'])) {
+                    $vars->setNode('%'.$var.'%', new \Twig_Node_Expression_Name($var, $body->getLine()));
+                }
+            }
+        }
+
+        return array(new \Twig_Node_Expression_Constant(str_replace('%%', '%', trim($msg)), $body->getLine()), $vars);
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/NodeVisitor/Scope.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/NodeVisitor/Scope.php
new file mode 100644 (file)
index 0000000..ce27b6a
--- /dev/null
@@ -0,0 +1,135 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\NodeVisitor;
+
+/**
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ */
+class Scope
+{
+    /**
+     * @var Scope|null
+     */
+    private $parent;
+
+    /**
+     * @var Scope[]
+     */
+    private $children;
+
+    /**
+     * @var array
+     */
+    private $data;
+
+    /**
+     * @var boolean
+     */
+    private $left;
+
+    /**
+     * @param Scope $parent
+     */
+    public function __construct(Scope $parent = null)
+    {
+        $this->parent = $parent;
+        $this->left = false;
+        $this->data = array();
+    }
+
+    /**
+     * Opens a new child scope.
+     *
+     * @return Scope
+     */
+    public function enter()
+    {
+        $child = new self($this);
+        $this->children[] = $child;
+
+        return $child;
+    }
+
+    /**
+     * Closes current scope and returns parent one.
+     *
+     * @return Scope|null
+     */
+    public function leave()
+    {
+        $this->left = true;
+
+        return $this->parent;
+    }
+
+    /**
+     * Stores data into current scope.
+     *
+     * @param string $key
+     * @param mixed  $value
+     *
+     * @throws \LogicException
+     *
+     * @return Scope Current scope
+     */
+    public function set($key, $value)
+    {
+        if ($this->left) {
+            throw new \LogicException('Left scope is not mutable.');
+        }
+
+        $this->data[$key] = $value;
+
+        return $this;
+    }
+
+    /**
+     * Tests if a data is visible from current scope.
+     *
+     * @param string $key
+     *
+     * @return boolean
+     */
+    public function has($key)
+    {
+        if (array_key_exists($key, $this->data)) {
+            return true;
+        }
+
+        if (null === $this->parent) {
+            return false;
+        }
+
+        return $this->parent->has($key);
+    }
+
+    /**
+     * Returns data visible from current scope.
+     *
+     * @param string $key
+     * @param mixed  $default
+     *
+     * @return mixed
+     */
+    public function get($key, $default = null)
+    {
+        if (array_key_exists($key, $this->data)) {
+            return $this->data[$key];
+        }
+
+        if (null === $this->parent) {
+            return $default;
+        }
+
+        return $this->parent->get($key, $default);
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php
new file mode 100644 (file)
index 0000000..8e7e7f4
--- /dev/null
@@ -0,0 +1,106 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\NodeVisitor;
+
+use Symfony\Bridge\Twig\Node\TransNode;
+use Symfony\Bridge\Twig\Node\TransDefaultDomainNode;
+
+/**
+ * TranslationDefaultDomainNodeVisitor.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class TranslationDefaultDomainNodeVisitor implements \Twig_NodeVisitorInterface
+{
+    /**
+     * @var Scope
+     */
+    private $scope;
+
+    /**
+     * Constructor.
+     */
+    public function __construct()
+    {
+        $this->scope = new Scope();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function enterNode(\Twig_NodeInterface $node, \Twig_Environment $env)
+    {
+        if ($node instanceof \Twig_Node_Block || $node instanceof \Twig_Node_Module) {
+            $this->scope = $this->scope->enter();
+        }
+
+        if ($node instanceof TransDefaultDomainNode) {
+            if ($node->getNode('expr') instanceof \Twig_Node_Expression_Constant) {
+                $this->scope->set('domain', $node->getNode('expr'));
+
+                return $node;
+            } else {
+                $var = $env->getParser()->getVarName();
+                $name = new \Twig_Node_Expression_AssignName($var, $node->getLine());
+                $this->scope->set('domain', new \Twig_Node_Expression_Name($var, $node->getLine()));
+
+                return new \Twig_Node_Set(false, new \Twig_Node(array($name)), new \Twig_Node(array($node->getNode('expr'))), $node->getLine());
+            }
+        }
+
+        if (!$this->scope->has('domain')) {
+            return $node;
+        }
+
+        if ($node instanceof \Twig_Node_Expression_Filter && in_array($node->getNode('filter')->getAttribute('value'), array('trans', 'transchoice'))) {
+            $ind = 'trans' === $node->getNode('filter')->getAttribute('value') ? 1 : 2;
+            $arguments = $node->getNode('arguments');
+            if (!$arguments->hasNode($ind)) {
+                if (!$arguments->hasNode($ind - 1)) {
+                    $arguments->setNode($ind - 1, new \Twig_Node_Expression_Array(array(), $node->getLine()));
+                }
+
+                $arguments->setNode($ind, $this->scope->get('domain'));
+            }
+        } elseif ($node instanceof TransNode) {
+            if (null === $node->getNode('domain')) {
+                $node->setNode('domain', $this->scope->get('domain'));
+            }
+        }
+
+        return $node;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function leaveNode(\Twig_NodeInterface $node, \Twig_Environment $env)
+    {
+        if ($node instanceof TransDefaultDomainNode) {
+            return false;
+        }
+
+        if ($node instanceof \Twig_Node_Block || $node instanceof \Twig_Node_Module) {
+            $this->scope = $this->scope->leave();
+        }
+
+        return $node;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getPriority()
+    {
+        return -10;
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/NodeVisitor/TranslationNodeVisitor.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/NodeVisitor/TranslationNodeVisitor.php
new file mode 100644 (file)
index 0000000..592c250
--- /dev/null
@@ -0,0 +1,137 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\NodeVisitor;
+
+use Symfony\Bridge\Twig\Node\TransNode;
+
+/**
+ * TranslationNodeVisitor extracts translation messages.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class TranslationNodeVisitor implements \Twig_NodeVisitorInterface
+{
+    const UNDEFINED_DOMAIN = '_undefined';
+
+    private $enabled = false;
+    private $messages = array();
+
+    public function enable()
+    {
+        $this->enabled = true;
+        $this->messages = array();
+    }
+
+    public function disable()
+    {
+        $this->enabled = false;
+        $this->messages = array();
+    }
+
+    public function getMessages()
+    {
+        return $this->messages;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function enterNode(\Twig_NodeInterface $node, \Twig_Environment $env)
+    {
+        if (!$this->enabled) {
+            return $node;
+        }
+
+        if (
+            $node instanceof \Twig_Node_Expression_Filter &&
+            'trans' === $node->getNode('filter')->getAttribute('value') &&
+            $node->getNode('node') instanceof \Twig_Node_Expression_Constant
+        ) {
+            // extract constant nodes with a trans filter
+            $this->messages[] = array(
+                $node->getNode('node')->getAttribute('value'),
+                $this->getReadDomainFromArguments($node->getNode('arguments'), 1),
+            );
+        } elseif (
+            $node instanceof \Twig_Node_Expression_Filter &&
+            'transchoice' === $node->getNode('filter')->getAttribute('value') &&
+            $node->getNode('node') instanceof \Twig_Node_Expression_Constant
+        ) {
+            // extract constant nodes with a trans filter
+            $this->messages[] = array(
+                $node->getNode('node')->getAttribute('value'),
+                $this->getReadDomainFromArguments($node->getNode('arguments'), 2),
+            );
+        } elseif ($node instanceof TransNode) {
+            // extract trans nodes
+            $this->messages[] = array(
+                $node->getNode('body')->getAttribute('data'),
+                $this->getReadDomainFromNode($node->getNode('domain')),
+            );
+        }
+
+        return $node;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function leaveNode(\Twig_NodeInterface $node, \Twig_Environment $env)
+    {
+        return $node;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getPriority()
+    {
+        return 0;
+    }
+
+    /**
+     * @param \Twig_Node $arguments
+     * @param int        $index
+     *
+     * @return string|null
+     */
+    private function getReadDomainFromArguments(\Twig_Node $arguments, $index)
+    {
+        if ($arguments->hasNode('domain')) {
+            $argument = $arguments->getNode('domain');
+        } elseif ($arguments->hasNode($index)) {
+            $argument = $arguments->getNode($index);
+        } else {
+            return null;
+        }
+
+        return $this->getReadDomainFromNode($argument);
+    }
+
+    /**
+     * @param \Twig_Node $node
+     *
+     * @return string|null
+     */
+    private function getReadDomainFromNode(\Twig_Node $node = null)
+    {
+        if (null === $node) {
+            return null;
+        }
+
+        if ($node instanceof \Twig_Node_Expression_Constant) {
+            return $node->getAttribute('value');
+        }
+
+        return self::UNDEFINED_DOMAIN;
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/README.md b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/README.md
new file mode 100644 (file)
index 0000000..e566323
--- /dev/null
@@ -0,0 +1,15 @@
+Twig Bridge
+===========
+
+Provides integration for [Twig](http://twig.sensiolabs.org/) with various
+Symfony2 components.
+
+Resources
+---------
+
+If you want to run the unit tests, install dev dependencies before
+running PHPUnit:
+
+    $ cd path/to/Symfony/Bridge/Twig/
+    $ composer.phar install --dev
+    $ phpunit
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig
new file mode 100644 (file)
index 0000000..453c29c
--- /dev/null
@@ -0,0 +1,390 @@
+{# Widgets #}
+
+{% block form_widget %}
+{% spaceless %}
+    {% if compound %}
+        {{ block('form_widget_compound') }}
+    {% else %}
+        {{ block('form_widget_simple') }}
+    {% endif %}
+{% endspaceless %}
+{% endblock form_widget %}
+
+{% block form_widget_simple %}
+{% spaceless %}
+    {% set type = type|default('text') %}
+    <input type="{{ type }}" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %}/>
+{% endspaceless %}
+{% endblock form_widget_simple %}
+
+{% block form_widget_compound %}
+{% spaceless %}
+    <div {{ block('widget_container_attributes') }}>
+        {% if form.parent is empty %}
+            {{ form_errors(form) }}
+        {% endif %}
+        {{ block('form_rows') }}
+        {{ form_rest(form) }}
+    </div>
+{% endspaceless %}
+{% endblock form_widget_compound %}
+
+{% block collection_widget %}
+{% spaceless %}
+    {% if prototype is defined %}
+        {% set attr = attr|merge({'data-prototype': form_row(prototype) }) %}
+    {% endif %}
+    {{ block('form_widget') }}
+{% endspaceless %}
+{% endblock collection_widget %}
+
+{% block textarea_widget %}
+{% spaceless %}
+    <textarea {{ block('widget_attributes') }}>{{ value }}</textarea>
+{% endspaceless %}
+{% endblock textarea_widget %}
+
+{% block choice_widget %}
+{% spaceless %}
+    {% if expanded %}
+        {{ block('choice_widget_expanded') }}
+    {% else %}
+        {{ block('choice_widget_collapsed') }}
+    {% endif %}
+{% endspaceless %}
+{% endblock choice_widget %}
+
+{% block choice_widget_expanded %}
+{% spaceless %}
+    <div {{ block('widget_container_attributes') }}>
+    {% for child in form %}
+        {{ form_widget(child) }}
+        {{ form_label(child) }}
+    {% endfor %}
+    </div>
+{% endspaceless %}
+{% endblock choice_widget_expanded %}
+
+{% block choice_widget_collapsed %}
+{% spaceless %}
+    <select {{ block('widget_attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
+        {% if empty_value is not none %}
+            <option {% if required %} disabled="disabled"{% if value is empty %} selected="selected"{% endif %}{% else %} value=""{% endif %}>{{ empty_value|trans({}, translation_domain) }}</option>
+        {% endif %}
+        {% if preferred_choices|length > 0 %}
+            {% set options = preferred_choices %}
+            {{ block('choice_widget_options') }}
+            {% if choices|length > 0 and separator is not none %}
+                <option disabled="disabled">{{ separator }}</option>
+            {% endif %}
+        {% endif %}
+        {% set options = choices %}
+        {{ block('choice_widget_options') }}
+    </select>
+{% endspaceless %}
+{% endblock choice_widget_collapsed %}
+
+{% block choice_widget_options %}
+{% spaceless %}
+    {% for group_label, choice in options %}
+        {% if choice is iterable %}
+            <optgroup label="{{ group_label|trans({}, translation_domain) }}">
+                {% set options = choice %}
+                {{ block('choice_widget_options') }}
+            </optgroup>
+        {% else %}
+            <option value="{{ choice.value }}"{% if choice is selectedchoice(value) %} selected="selected"{% endif %}>{{ choice.label|trans({}, translation_domain) }}</option>
+        {% endif %}
+    {% endfor %}
+{% endspaceless %}
+{% endblock choice_widget_options %}
+
+{% block checkbox_widget %}
+{% spaceless %}
+    <input type="checkbox" {{ block('widget_attributes') }}{% if value is defined %} value="{{ value }}"{% endif %}{% if checked %} checked="checked"{% endif %} />
+{% endspaceless %}
+{% endblock checkbox_widget %}
+
+{% block radio_widget %}
+{% spaceless %}
+    <input type="radio" {{ block('widget_attributes') }}{% if value is defined %} value="{{ value }}"{% endif %}{% if checked %} checked="checked"{% endif %} />
+{% endspaceless %}
+{% endblock radio_widget %}
+
+{% block datetime_widget %}
+{% spaceless %}
+    {% if widget == 'single_text' %}
+        {{ block('form_widget_simple') }}
+    {% else %}
+        <div {{ block('widget_container_attributes') }}>
+            {{ form_errors(form.date) }}
+            {{ form_errors(form.time) }}
+            {{ form_widget(form.date) }}
+            {{ form_widget(form.time) }}
+        </div>
+    {% endif %}
+{% endspaceless %}
+{% endblock datetime_widget %}
+
+{% block date_widget %}
+{% spaceless %}
+    {% if widget == 'single_text' %}
+        {{ block('form_widget_simple') }}
+    {% else %}
+        <div {{ block('widget_container_attributes') }}>
+            {{ date_pattern|replace({
+                '{{ year }}':  form_widget(form.year),
+                '{{ month }}': form_widget(form.month),
+                '{{ day }}':   form_widget(form.day),
+            })|raw }}
+        </div>
+    {% endif %}
+{% endspaceless %}
+{% endblock date_widget %}
+
+{% block time_widget %}
+{% spaceless %}
+    {% if widget == 'single_text' %}
+        {{ block('form_widget_simple') }}
+    {% else %}
+        {% set vars = widget == 'text' ? { 'attr': { 'size': 1 }} : {} %}
+        <div {{ block('widget_container_attributes') }}>
+            {{ form_widget(form.hour, vars) }}{% if with_minutes %}:{{ form_widget(form.minute, vars) }}{% endif %}{% if with_seconds %}:{{ form_widget(form.second, vars) }}{% endif %}
+        </div>
+    {% endif %}
+{% endspaceless %}
+{% endblock time_widget %}
+
+{% block number_widget %}
+{% spaceless %}
+    {# type="number" doesn't work with floats #}
+    {% set type = type|default('text') %}
+    {{ block('form_widget_simple') }}
+{% endspaceless %}
+{% endblock number_widget %}
+
+{% block integer_widget %}
+{% spaceless %}
+    {% set type = type|default('number') %}
+    {{ block('form_widget_simple') }}
+{% endspaceless %}
+{% endblock integer_widget %}
+
+{% block money_widget %}
+{% spaceless %}
+    {{ money_pattern|replace({ '{{ widget }}': block('form_widget_simple') })|raw }}
+{% endspaceless %}
+{% endblock money_widget %}
+
+{% block url_widget %}
+{% spaceless %}
+    {% set type = type|default('url') %}
+    {{ block('form_widget_simple') }}
+{% endspaceless %}
+{% endblock url_widget %}
+
+{% block search_widget %}
+{% spaceless %}
+    {% set type = type|default('search') %}
+    {{ block('form_widget_simple') }}
+{% endspaceless %}
+{% endblock search_widget %}
+
+{% block percent_widget %}
+{% spaceless %}
+    {% set type = type|default('text') %}
+    {{ block('form_widget_simple') }} %
+{% endspaceless %}
+{% endblock percent_widget %}
+
+{% block password_widget %}
+{% spaceless %}
+    {% set type = type|default('password') %}
+    {{ block('form_widget_simple') }}
+{% endspaceless %}
+{% endblock password_widget %}
+
+{% block hidden_widget %}
+{% spaceless %}
+    {% set type = type|default('hidden') %}
+    {{ block('form_widget_simple') }}
+{% endspaceless %}
+{% endblock hidden_widget %}
+
+{% block email_widget %}
+{% spaceless %}
+    {% set type = type|default('email') %}
+    {{ block('form_widget_simple') }}
+{% endspaceless %}
+{% endblock email_widget %}
+
+{% block button_widget %}
+{% spaceless %}
+    {% if label is empty %}
+        {% set label = name|humanize %}
+    {% endif %}
+    <button type="{{ type|default('button') }}" {{ block('button_attributes') }}>{{ label|trans({}, translation_domain) }}</button>
+{% endspaceless %}
+{% endblock button_widget %}
+
+{% block submit_widget %}
+{% spaceless %}
+    {% set type = type|default('submit') %}
+    {{ block('button_widget') }}
+{% endspaceless %}
+{% endblock submit_widget %}
+
+{% block reset_widget %}
+{% spaceless %}
+    {% set type = type|default('reset') %}
+    {{ block('button_widget') }}
+{% endspaceless %}
+{% endblock reset_widget %}
+
+{# Labels #}
+
+{% block form_label %}
+{% spaceless %}
+    {% if label is not sameas(false) %}
+        {% if not compound %}
+            {% set label_attr = label_attr|merge({'for': id}) %}
+        {% endif %}
+        {% if required %}
+            {% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ ' required')|trim}) %}
+        {% endif %}
+        {% if label is empty %}
+            {% set label = name|humanize %}
+        {% endif %}
+        <label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>{{ label|trans({}, translation_domain) }}</label>
+    {% endif %}
+{% endspaceless %}
+{% endblock form_label %}
+
+{% block button_label %}{% endblock %}
+
+{# Rows #}
+
+{% block repeated_row %}
+{% spaceless %}
+    {#
+    No need to render the errors here, as all errors are mapped
+    to the first child (see RepeatedTypeValidatorExtension).
+    #}
+    {{ block('form_rows') }}
+{% endspaceless %}
+{% endblock repeated_row %}
+
+{% block form_row %}
+{% spaceless %}
+    <div>
+        {{ form_label(form) }}
+        {{ form_errors(form) }}
+        {{ form_widget(form) }}
+    </div>
+{% endspaceless %}
+{% endblock form_row %}
+
+{% block button_row %}
+{% spaceless %}
+    <div>
+        {{ form_widget(form) }}
+    </div>
+{% endspaceless %}
+{% endblock button_row %}
+
+{% block hidden_row %}
+    {{ form_widget(form) }}
+{% endblock hidden_row %}
+
+{# Misc #}
+
+{% block form %}
+{% spaceless %}
+    {{ form_start(form) }}
+        {{ form_widget(form) }}
+    {{ form_end(form) }}
+{% endspaceless %}
+{% endblock form %}
+
+{% block form_start %}
+{% spaceless %}
+    {% set method = method|upper %}
+    {% if method in ["GET", "POST"] %}
+        {% set form_method = method %}
+    {% else %}
+        {% set form_method = "POST" %}
+    {% endif %}
+    <form method="{{ form_method|lower }}" action="{{ action }}"{% for attrname, attrvalue in attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}{% if multipart %} enctype="multipart/form-data"{% endif %}>
+    {% if form_method != method %}
+        <input type="hidden" name="_method" value="{{ method }}" />
+    {% endif %}
+{% endspaceless %}
+{% endblock form_start %}
+
+{% block form_end %}
+{% spaceless %}
+    {% if not render_rest is defined or render_rest %}
+        {{ form_rest(form) }}
+    {% endif %}
+    </form>
+{% endspaceless %}
+{% endblock form_end %}
+
+{% block form_enctype %}
+{% spaceless %}
+    {% if multipart %}enctype="multipart/form-data"{% endif %}
+{% endspaceless %}
+{% endblock form_enctype %}
+
+{% block form_errors %}
+{% spaceless %}
+    {% if errors|length > 0 %}
+    <ul>
+        {% for error in errors %}
+            <li>{{ error.message }}</li>
+        {% endfor %}
+    </ul>
+    {% endif %}
+{% endspaceless %}
+{% endblock form_errors %}
+
+{% block form_rest %}
+{% spaceless %}
+    {% for child in form %}
+        {% if not child.rendered %}
+            {{ form_row(child) }}
+        {% endif %}
+    {% endfor %}
+{% endspaceless %}
+{% endblock form_rest %}
+
+{# Support #}
+
+{% block form_rows %}
+{% spaceless %}
+    {% for child in form %}
+        {{ form_row(child) }}
+    {% endfor %}
+{% endspaceless %}
+{% endblock form_rows %}
+
+{% block widget_attributes %}
+{% spaceless %}
+    id="{{ id }}" name="{{ full_name }}"{% if read_only %} readonly="readonly"{% endif %}{% if disabled %} disabled="disabled"{% endif %}{% if required %} required="required"{% endif %}{% if max_length %} maxlength="{{ max_length }}"{% endif %}{% if pattern %} pattern="{{ pattern }}"{% endif %}
+    {% for attrname, attrvalue in attr %}{% if attrname in ['placeholder', 'title'] %}{{ attrname }}="{{ attrvalue|trans({}, translation_domain) }}" {% else %}{{ attrname }}="{{ attrvalue }}" {% endif %}{% endfor %}
+{% endspaceless %}
+{% endblock widget_attributes %}
+
+{% block widget_container_attributes %}
+{% spaceless %}
+    {% if id is not empty %}id="{{ id }}" {% endif %}
+    {% for attrname, attrvalue in attr %}{{ attrname }}="{{ attrvalue }}" {% endfor %}
+{% endspaceless %}
+{% endblock widget_container_attributes %}
+
+{% block button_attributes %}
+{% spaceless %}
+    id="{{ id }}" name="{{ full_name }}"{% if disabled %} disabled="disabled"{% endif %}
+    {% for attrname, attrvalue in attr %}{{ attrname }}="{{ attrvalue }}" {% endfor %}
+{% endspaceless %}
+{% endblock button_attributes %}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig
new file mode 100644 (file)
index 0000000..aed4f8d
--- /dev/null
@@ -0,0 +1,52 @@
+{% use "form_div_layout.html.twig" %}
+
+{% block form_row %}
+{% spaceless %}
+    <tr>
+        <td>
+            {{ form_label(form) }}
+        </td>
+        <td>
+            {{ form_errors(form) }}
+            {{ form_widget(form) }}
+        </td>
+    </tr>
+{% endspaceless %}
+{% endblock form_row %}
+
+{% block button_row %}
+{% spaceless %}
+    <tr>
+        <td></td>
+        <td>
+            {{ form_widget(form) }}
+        </td>
+    </tr>
+{% endspaceless %}
+{% endblock button_row %}
+
+{% block hidden_row %}
+{% spaceless %}
+    <tr style="display: none">
+        <td colspan="2">
+            {{ form_widget(form) }}
+        </td>
+    </tr>
+{% endspaceless %}
+{% endblock hidden_row %}
+
+{% block form_widget_compound %}
+{% spaceless %}
+    <table {{ block('widget_container_attributes') }}>
+        {% if form.parent is empty and errors|length > 0 %}
+        <tr>
+            <td colspan="2">
+                {{ form_errors(form) }}
+            </td>
+        </tr>
+        {% endif %}
+        {{ block('form_rows') }}
+        {{ form_rest(form) }}
+    </table>
+{% endspaceless %}
+{% endblock form_widget_compound %}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/CodeExtensionTest.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/CodeExtensionTest.php
new file mode 100644 (file)
index 0000000..d935651
--- /dev/null
@@ -0,0 +1,69 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Tests\Extension;
+
+use Symfony\Bridge\Twig\Extension\CodeExtension;
+
+class CodeExtensionTest extends \PHPUnit_Framework_TestCase
+{
+    protected $helper;
+
+    public function testFormatFile()
+    {
+        $expected = sprintf('<a href="txmt://open?url=file://%s&amp;line=25" title="Click to open this file" class="file_link">%s at line 25</a>', __FILE__, __FILE__);
+        $this->assertEquals($expected, $this->getExtension()->formatFile(__FILE__, 25));
+    }
+
+    /**
+     * @dataProvider getClassNameProvider
+     */
+    public function testGettingClassAbbreviation($class, $abbr)
+    {
+        $this->assertEquals($this->getExtension()->abbrClass($class), $abbr);
+    }
+
+    /**
+     * @dataProvider getMethodNameProvider
+     */
+    public function testGettingMethodAbbreviation($method, $abbr)
+    {
+        $this->assertEquals($this->getExtension()->abbrMethod($method), $abbr);
+    }
+
+    public function getClassNameProvider()
+    {
+        return array(
+            array('F\Q\N\Foo', '<abbr title="F\Q\N\Foo">Foo</abbr>'),
+            array('Bare', '<abbr title="Bare">Bare</abbr>'),
+        );
+    }
+
+    public function getMethodNameProvider()
+    {
+        return array(
+            array('F\Q\N\Foo::Method', '<abbr title="F\Q\N\Foo">Foo</abbr>::Method()'),
+            array('Bare::Method', '<abbr title="Bare">Bare</abbr>::Method()'),
+            array('Closure', '<abbr title="Closure">Closure</abbr>'),
+            array('Method', '<abbr title="Method">Method</abbr>()')
+        );
+    }
+
+    public function testGetName()
+    {
+        $this->assertEquals('code', $this->getExtension()->getName());
+    }
+
+    protected function getExtension()
+    {
+        return new CodeExtension('txmt://open?url=file://%f&line=%l', '/root', 'UTF-8');
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubFilesystemLoader.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubFilesystemLoader.php
new file mode 100644 (file)
index 0000000..36c61cd
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Tests\Extension\Fixtures;
+
+// Preventing autoloader throwing E_FATAL when Twig is now available
+if (!class_exists('Twig_Environment')) {
+    class StubFilesystemLoader
+    {
+    }
+} else {
+    class StubFilesystemLoader extends \Twig_Loader_Filesystem
+    {
+        protected function findTemplate($name)
+        {
+            // strip away bundle name
+            $parts = explode(':', $name);
+
+            return parent::findTemplate(end($parts));
+        }
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubTranslator.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubTranslator.php
new file mode 100644 (file)
index 0000000..b7d011b
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Tests\Extension\Fixtures;
+
+use Symfony\Component\Translation\TranslatorInterface;
+
+class StubTranslator implements TranslatorInterface
+{
+    public function trans($id, array $parameters = array(), $domain = null, $locale = null)
+    {
+        return '[trans]'.$id.'[/trans]';
+    }
+
+    public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null)
+    {
+        return '[trans]'.$id.'[/trans]';
+    }
+
+    public function setLocale($locale)
+    {
+    }
+
+    public function getLocale()
+    {
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php
new file mode 100644 (file)
index 0000000..c5c134b
--- /dev/null
@@ -0,0 +1,209 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Tests\Extension;
+
+use Symfony\Bridge\Twig\Extension\FormExtension;
+use Symfony\Bridge\Twig\Form\TwigRenderer;
+use Symfony\Bridge\Twig\Form\TwigRendererEngine;
+use Symfony\Bridge\Twig\Extension\TranslationExtension;
+use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubTranslator;
+use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubFilesystemLoader;
+use Symfony\Component\Form\FormView;
+use Symfony\Component\Form\Extension\Core\View\ChoiceView;
+use Symfony\Component\Form\Tests\AbstractDivLayoutTest;
+
+class FormExtensionDivLayoutTest extends AbstractDivLayoutTest
+{
+    /**
+     * @var FormExtension
+     */
+    protected $extension;
+
+    protected function setUp()
+    {
+        if (!class_exists('Symfony\Component\Locale\Locale')) {
+            $this->markTestSkipped('The "Locale" component is not available');
+        }
+
+        if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) {
+            $this->markTestSkipped('The "EventDispatcher" component is not available');
+        }
+
+        if (!class_exists('Symfony\Component\Form\Form')) {
+            $this->markTestSkipped('The "Form" component is not available');
+        }
+
+        if (!class_exists('Twig_Environment')) {
+            $this->markTestSkipped('Twig is not available.');
+        }
+
+        parent::setUp();
+
+        $rendererEngine = new TwigRendererEngine(array(
+            'form_div_layout.html.twig',
+            'custom_widgets.html.twig',
+        ));
+        $renderer = new TwigRenderer($rendererEngine, $this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface'));
+
+        $this->extension = new FormExtension($renderer);
+
+        $loader = new StubFilesystemLoader(array(
+            __DIR__.'/../../Resources/views/Form',
+            __DIR__,
+        ));
+
+        $environment = new \Twig_Environment($loader, array('strict_variables' => true));
+        $environment->addExtension(new TranslationExtension(new StubTranslator()));
+        $environment->addGlobal('global', '');
+        $environment->addExtension($this->extension);
+
+        $this->extension->initRuntime($environment);
+    }
+
+    protected function tearDown()
+    {
+        parent::tearDown();
+
+        $this->extension = null;
+    }
+
+    public function testThemeBlockInheritanceUsingUse()
+    {
+        $view = $this->factory
+            ->createNamed('name', 'email')
+            ->createView()
+        ;
+
+        $this->setTheme($view, array('theme_use.html.twig'));
+
+        $this->assertMatchesXpath(
+            $this->renderWidget($view),
+            '/input[@type="email"][@rel="theme"]'
+        );
+    }
+
+    public function testThemeBlockInheritanceUsingExtend()
+    {
+        $view = $this->factory
+            ->createNamed('name', 'email')
+            ->createView()
+        ;
+
+        $this->setTheme($view, array('theme_extends.html.twig'));
+
+        $this->assertMatchesXpath(
+            $this->renderWidget($view),
+            '/input[@type="email"][@rel="theme"]'
+        );
+    }
+
+    public function isSelectedChoiceProvider()
+    {
+        // The commented cases should not be necessary anymore, because the
+        // choice lists should assure that both values passed here are always
+        // strings
+        return array(
+//             array(true, 0, 0),
+            array(true, '0', '0'),
+            array(true, '1', '1'),
+//             array(true, false, 0),
+//             array(true, true, 1),
+            array(true, '', ''),
+//             array(true, null, ''),
+            array(true, '1.23', '1.23'),
+            array(true, 'foo', 'foo'),
+            array(true, 'foo10', 'foo10'),
+            array(true, 'foo', array(1, 'foo', 'foo10')),
+
+            array(false, 10, array(1, 'foo', 'foo10')),
+            array(false, 0, array(1, 'foo', 'foo10')),
+        );
+    }
+
+    /**
+     * @dataProvider isSelectedChoiceProvider
+     */
+    public function testIsChoiceSelected($expected, $choice, $value)
+    {
+        $choice = new ChoiceView($choice, $choice, $choice.' label');
+
+        $this->assertSame($expected, $this->extension->isSelectedChoice($choice, $value));
+    }
+
+    protected function renderForm(FormView $view, array $vars = array())
+    {
+        return (string) $this->extension->renderer->renderBlock($view, 'form', $vars);
+    }
+
+    protected function renderEnctype(FormView $view)
+    {
+        return (string) $this->extension->renderer->searchAndRenderBlock($view, 'enctype');
+    }
+
+    protected function renderLabel(FormView $view, $label = null, array $vars = array())
+    {
+        if ($label !== null) {
+            $vars += array('label' => $label);
+        }
+
+        return (string) $this->extension->renderer->searchAndRenderBlock($view, 'label', $vars);
+    }
+
+    protected function renderErrors(FormView $view)
+    {
+        return (string) $this->extension->renderer->searchAndRenderBlock($view, 'errors');
+    }
+
+    protected function renderWidget(FormView $view, array $vars = array())
+    {
+        return (string) $this->extension->renderer->searchAndRenderBlock($view, 'widget', $vars);
+    }
+
+    protected function renderRow(FormView $view, array $vars = array())
+    {
+        return (string) $this->extension->renderer->searchAndRenderBlock($view, 'row', $vars);
+    }
+
+    protected function renderRest(FormView $view, array $vars = array())
+    {
+        return (string) $this->extension->renderer->searchAndRenderBlock($view, 'rest', $vars);
+    }
+
+    protected function renderStart(FormView $view, array $vars = array())
+    {
+        return (string) $this->extension->renderer->renderBlock($view, 'form_start', $vars);
+    }
+
+    protected function renderEnd(FormView $view, array $vars = array())
+    {
+        return (string) $this->extension->renderer->renderBlock($view, 'form_end', $vars);
+    }
+
+    protected function setTheme(FormView $view, array $themes)
+    {
+        $this->extension->renderer->setTheme($view, $themes);
+    }
+
+    public static function themeBlockInheritanceProvider()
+    {
+        return array(
+            array(array('theme.html.twig'))
+        );
+    }
+
+    public static function themeInheritanceProvider()
+    {
+        return array(
+            array(array('parent_label.html.twig'), array('child_label.html.twig'))
+        );
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php
new file mode 100644 (file)
index 0000000..99a7821
--- /dev/null
@@ -0,0 +1,131 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Tests\Extension;
+
+use Symfony\Component\Form\FormView;
+use Symfony\Bridge\Twig\Form\TwigRenderer;
+use Symfony\Bridge\Twig\Form\TwigRendererEngine;
+use Symfony\Bridge\Twig\Extension\FormExtension;
+use Symfony\Bridge\Twig\Extension\TranslationExtension;
+use Symfony\Component\Form\Tests\AbstractTableLayoutTest;
+use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubTranslator;
+use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubFilesystemLoader;
+
+class FormExtensionTableLayoutTest extends AbstractTableLayoutTest
+{
+    /**
+     * @var FormExtension
+     */
+    protected $extension;
+
+    protected function setUp()
+    {
+        if (!class_exists('Symfony\Component\Locale\Locale')) {
+            $this->markTestSkipped('The "Locale" component is not available');
+        }
+
+        if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) {
+            $this->markTestSkipped('The "EventDispatcher" component is not available');
+        }
+
+        if (!class_exists('Symfony\Component\Form\Form')) {
+            $this->markTestSkipped('The "Form" component is not available');
+        }
+
+        if (!class_exists('Twig_Environment')) {
+            $this->markTestSkipped('Twig is not available.');
+        }
+
+        parent::setUp();
+
+        $rendererEngine = new TwigRendererEngine(array(
+            'form_table_layout.html.twig',
+            'custom_widgets.html.twig',
+        ));
+        $renderer = new TwigRenderer($rendererEngine, $this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface'));
+
+        $this->extension = new FormExtension($renderer);
+
+        $loader = new StubFilesystemLoader(array(
+            __DIR__.'/../../Resources/views/Form',
+            __DIR__,
+        ));
+
+        $environment = new \Twig_Environment($loader, array('strict_variables' => true));
+        $environment->addExtension(new TranslationExtension(new StubTranslator()));
+        $environment->addGlobal('global', '');
+        $environment->addExtension($this->extension);
+
+        $this->extension->initRuntime($environment);
+    }
+
+    protected function tearDown()
+    {
+        parent::tearDown();
+
+        $this->extension = null;
+    }
+
+    protected function renderForm(FormView $view, array $vars = array())
+    {
+        return (string) $this->extension->renderer->renderBlock($view, 'form', $vars);
+    }
+
+    protected function renderEnctype(FormView $view)
+    {
+        return (string) $this->extension->renderer->searchAndRenderBlock($view, 'enctype');
+    }
+
+    protected function renderLabel(FormView $view, $label = null, array $vars = array())
+    {
+        if ($label !== null) {
+            $vars += array('label' => $label);
+        }
+
+        return (string) $this->extension->renderer->searchAndRenderBlock($view, 'label', $vars);
+    }
+
+    protected function renderErrors(FormView $view)
+    {
+        return (string) $this->extension->renderer->searchAndRenderBlock($view, 'errors');
+    }
+
+    protected function renderWidget(FormView $view, array $vars = array())
+    {
+        return (string) $this->extension->renderer->searchAndRenderBlock($view, 'widget', $vars);
+    }
+
+    protected function renderRow(FormView $view, array $vars = array())
+    {
+        return (string) $this->extension->renderer->searchAndRenderBlock($view, 'row', $vars);
+    }
+
+    protected function renderRest(FormView $view, array $vars = array())
+    {
+        return (string) $this->extension->renderer->searchAndRenderBlock($view, 'rest', $vars);
+    }
+
+    protected function renderStart(FormView $view, array $vars = array())
+    {
+        return (string) $this->extension->renderer->renderBlock($view, 'form_start', $vars);
+    }
+
+    protected function renderEnd(FormView $view, array $vars = array())
+    {
+        return (string) $this->extension->renderer->renderBlock($view, 'form_end', $vars);
+    }
+
+    protected function setTheme(FormView $view, array $themes)
+    {
+        $this->extension->renderer->setTheme($view, $themes);
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php
new file mode 100644 (file)
index 0000000..077927c
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Tests\Extension;
+
+use Symfony\Bridge\Twig\Extension\HttpKernelExtension;
+use Symfony\Bridge\Twig\Tests\TestCase;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\Fragment\FragmentHandler;
+
+class HttpKernelExtensionTest extends TestCase
+{
+    protected function setUp()
+    {
+        parent::setUp();
+
+        if (!class_exists('Symfony\Component\HttpKernel\HttpKernel')) {
+            $this->markTestSkipped('The "HttpKernel" component is not available');
+        }
+
+        if (!class_exists('Twig_Environment')) {
+            $this->markTestSkipped('Twig is not available.');
+        }
+    }
+
+    /**
+     * @expectedException \Twig_Error_Runtime
+     */
+    public function testFragmentWithError()
+    {
+        $kernel = $this->getFragmentHandler($this->throwException(new \Exception('foo')));
+
+        $loader = new \Twig_Loader_Array(array('index' => '{{ fragment("foo") }}'));
+        $twig = new \Twig_Environment($loader, array('debug' => true, 'cache' => false));
+        $twig->addExtension(new HttpKernelExtension($kernel));
+
+        $this->renderTemplate($kernel);
+    }
+
+    protected function getFragmentHandler($return)
+    {
+        $strategy = $this->getMock('Symfony\\Component\\HttpKernel\\Fragment\\FragmentRendererInterface');
+        $strategy->expects($this->once())->method('getName')->will($this->returnValue('inline'));
+        $strategy->expects($this->once())->method('render')->will($return);
+
+        $renderer = new FragmentHandler(array($strategy));
+        $renderer->setRequest(Request::create('/'));
+
+        return $renderer;
+    }
+
+    protected function renderTemplate(FragmentHandler $renderer, $template = '{{ render("foo") }}')
+    {
+        $loader = new \Twig_Loader_Array(array('index' => $template));
+        $twig = new \Twig_Environment($loader, array('debug' => true, 'cache' => false));
+        $twig->addExtension(new HttpKernelExtension($renderer));
+
+        return $twig->render('index');
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/RoutingExtensionTest.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/RoutingExtensionTest.php
new file mode 100644 (file)
index 0000000..3c5d762
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Tests\Extension;
+
+use Symfony\Bridge\Twig\Extension\RoutingExtension;
+use Symfony\Bridge\Twig\Tests\TestCase;
+
+class RoutingExtensionTest extends TestCase
+{
+    protected function setUp()
+    {
+        parent::setUp();
+
+        if (!class_exists('Symfony\Component\Routing\Route')) {
+            $this->markTestSkipped('The "Routing" component is not available');
+        }
+    }
+
+    /**
+     * @dataProvider getEscapingTemplates
+     */
+    public function testEscaping($template, $mustBeEscaped)
+    {
+        $twig = new \Twig_Environment(null, array('debug' => true, 'cache' => false, 'autoescape' => true, 'optimizations' => 0));
+        $twig->addExtension(new RoutingExtension($this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface')));
+
+        $nodes = $twig->parse($twig->tokenize($template));
+
+        $this->assertSame($mustBeEscaped, $nodes->getNode('body')->getNode(0)->getNode('expr') instanceof \Twig_Node_Expression_Filter);
+    }
+
+    public function getEscapingTemplates()
+    {
+        return array(
+            array('{{ path("foo") }}', false),
+            array('{{ path("foo", {}) }}', false),
+            array('{{ path("foo", { foo: "foo" }) }}', false),
+            array('{{ path("foo", foo) }}', true),
+            array('{{ path("foo", { foo: foo }) }}', true),
+            array('{{ path("foo", { foo: ["foo", "bar"] }) }}', true),
+            array('{{ path("foo", { foo: "foo", bar: "bar" }) }}', true),
+
+            array('{{ path(name = "foo", parameters = {}) }}', false),
+            array('{{ path(name = "foo", parameters = { foo: "foo" }) }}', false),
+            array('{{ path(name = "foo", parameters = foo) }}', true),
+            array('{{ path(name = "foo", parameters = { foo: ["foo", "bar"] }) }}', true),
+            array('{{ path(name = "foo", parameters = { foo: foo }) }}', true),
+            array('{{ path(name = "foo", parameters = { foo: "foo", bar: "bar" }) }}', true),
+        );
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php
new file mode 100644 (file)
index 0000000..2b9c553
--- /dev/null
@@ -0,0 +1,151 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Tests\Extension;
+
+use Symfony\Bridge\Twig\Extension\TranslationExtension;
+use Symfony\Component\Translation\Translator;
+use Symfony\Component\Translation\MessageSelector;
+use Symfony\Component\Translation\Loader\ArrayLoader;
+use Symfony\Bridge\Twig\Tests\TestCase;
+
+class TranslationExtensionTest extends TestCase
+{
+    protected function setUp()
+    {
+        parent::setUp();
+
+        if (!class_exists('Symfony\Component\Translation\Translator')) {
+            $this->markTestSkipped('The "Translation" component is not available');
+        }
+
+        if (!class_exists('Twig_Environment')) {
+            $this->markTestSkipped('Twig is not available.');
+        }
+    }
+
+    public function testEscaping()
+    {
+        $output = $this->getTemplate('{% trans %}Percent: %value%%% (%msg%){% endtrans %}')->render(array('value' => 12, 'msg' => 'approx.'));
+
+        $this->assertEquals('Percent: 12% (approx.)', $output);
+    }
+
+    /**
+     * @dataProvider getTransTests
+     */
+    public function testTrans($template, $expected, array $variables = array())
+    {
+        if ($expected != $this->getTemplate($template)->render($variables)) {
+            print $template."\n";
+            $loader = new \Twig_Loader_Array(array('index' => $template));
+            $twig = new \Twig_Environment($loader, array('debug' => true, 'cache' => false));
+            $twig->addExtension(new TranslationExtension(new Translator('en', new MessageSelector())));
+
+            echo $twig->compile($twig->parse($twig->tokenize($twig->getLoader()->getSource('index'), 'index')))."\n\n";
+            $this->assertEquals($expected, $this->getTemplate($template)->render($variables));
+        }
+
+        $this->assertEquals($expected, $this->getTemplate($template)->render($variables));
+    }
+
+    public function getTransTests()
+    {
+        return array(
+            // trans tag
+            array('{% trans %}Hello{% endtrans %}', 'Hello'),
+            array('{% trans %}%name%{% endtrans %}', 'Symfony2', array('name' => 'Symfony2')),
+
+            array('{% trans from elsewhere %}Hello{% endtrans %}', 'Hello'),
+
+            array('{% trans %}Hello %name%{% endtrans %}', 'Hello Symfony2', array('name' => 'Symfony2')),
+            array('{% trans with { \'%name%\': \'Symfony2\' } %}Hello %name%{% endtrans %}', 'Hello Symfony2'),
+            array('{% set vars = { \'%name%\': \'Symfony2\' } %}{% trans with vars %}Hello %name%{% endtrans %}', 'Hello Symfony2'),
+
+            array('{% trans into "fr"%}Hello{% endtrans %}', 'Hello'),
+
+            // transchoice
+            array('{% transchoice count from "messages" %}{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples{% endtranschoice %}',
+                'There is no apples', array('count' => 0)),
+            array('{% transchoice count %}{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples{% endtranschoice %}',
+                'There is 5 apples', array('count' => 5)),
+            array('{% transchoice count %}{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples (%name%){% endtranschoice %}',
+                'There is 5 apples (Symfony2)', array('count' => 5, 'name' => 'Symfony2')),
+            array('{% transchoice count with { \'%name%\': \'Symfony2\' } %}{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples (%name%){% endtranschoice %}',
+                'There is 5 apples (Symfony2)', array('count' => 5)),
+            array('{% transchoice count into "fr"%}{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples{% endtranschoice %}',
+                'There is no apples', array('count' => 0)),
+
+            // trans filter
+            array('{{ "Hello"|trans }}', 'Hello'),
+            array('{{ name|trans }}', 'Symfony2', array('name' => 'Symfony2')),
+            array('{{ hello|trans({ \'%name%\': \'Symfony2\' }) }}', 'Hello Symfony2', array('hello' => 'Hello %name%')),
+            array('{% set vars = { \'%name%\': \'Symfony2\' } %}{{ hello|trans(vars) }}', 'Hello Symfony2', array('hello' => 'Hello %name%')),
+            array('{{ "Hello"|trans({}, "messages", "fr") }}', 'Hello'),
+
+            // transchoice filter
+            array('{{ "{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples"|transchoice(count) }}', 'There is 5 apples', array('count' => 5)),
+            array('{{ text|transchoice(5, {\'%name%\': \'Symfony2\'}) }}', 'There is 5 apples (Symfony2)', array('text' => '{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples (%name%)')),
+            array('{{ "{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples"|transchoice(count, {}, "messages", "fr") }}', 'There is 5 apples', array('count' => 5)),
+        );
+    }
+
+    public function testDefaultTranslationDomain()
+    {
+        $templates = array(
+            'index' => '
+                {%- extends "base" %}
+
+                {%- trans_default_domain "foo" %}
+
+                {%- block content %}
+                    {%- trans %}foo{% endtrans %}
+                    {%- trans from "custom" %}foo{% endtrans %}
+                    {{- "foo"|trans }}
+                    {{- "foo"|trans({}, "custom") }}
+                    {{- "foo"|transchoice(1) }}
+                    {{- "foo"|transchoice(1, {}, "custom") }}
+                {% endblock %}
+            ',
+
+            'base' => '
+                {%- block content "" %}
+            ',
+        );
+
+        $translator = new Translator('en', new MessageSelector());
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', array('foo' => 'foo (messages)'), 'en');
+        $translator->addResource('array', array('foo' => 'foo (custom)'), 'en', 'custom');
+        $translator->addResource('array', array('foo' => 'foo (foo)'), 'en', 'foo');
+
+        $template = $this->getTemplate($templates, $translator);
+
+        $this->assertEquals('foo (foo)foo (custom)foo (foo)foo (custom)foo (foo)foo (custom)', trim($template->render(array())));
+    }
+
+    protected function getTemplate($template, $translator = null)
+    {
+        if (null === $translator) {
+            $translator = new Translator('en', new MessageSelector());
+        }
+
+        if (is_array($template)) {
+            $loader = new \Twig_Loader_Array($template);
+        } else {
+            $loader = new \Twig_Loader_Array(array('index' => $template));
+        }
+        $twig = new \Twig_Environment($loader, array('debug' => true, 'cache' => false));
+        $twig->addExtension(new TranslationExtension($translator));
+
+        return $twig->loadTemplate('index');
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/child_label.html.twig b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/child_label.html.twig
new file mode 100644 (file)
index 0000000..8c7c248
--- /dev/null
@@ -0,0 +1,3 @@
+{% block form_label %}
+    <label>{{ global }}child</label>
+{% endblock form_label %}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/custom_widgets.html.twig b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/custom_widgets.html.twig
new file mode 100644 (file)
index 0000000..12fd7c6
--- /dev/null
@@ -0,0 +1,16 @@
+{% block _text_id_widget %}
+{% spaceless %}
+    <div id="container">
+        {{ form_widget(form) }}
+    </div>
+{% endspaceless %}
+{% endblock _text_id_widget %}
+
+{% block _name_entry_label %}
+{% spaceless %}
+    {% if label is empty %}
+        {% set label = name|humanize %}
+    {% endif %}
+    <label>Custom label: {{ label|trans({}, translation_domain) }}</label>
+{% endspaceless %}
+{% endblock _name_entry_label %}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/parent_label.html.twig b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/parent_label.html.twig
new file mode 100644 (file)
index 0000000..e96278b
--- /dev/null
@@ -0,0 +1,3 @@
+{% block form_label %}
+    <label>parent</label>
+{% endblock form_label %}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/theme.html.twig b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/theme.html.twig
new file mode 100644 (file)
index 0000000..da1c1b6
--- /dev/null
@@ -0,0 +1,6 @@
+{% block form_widget_simple %}
+{% spaceless %}
+    {% set type = type|default('text') %}
+    <input type="{{ type }}" {{ block('widget_attributes') }} value="{{ value }}" rel="theme" />
+{% endspaceless %}
+{% endblock form_widget_simple %}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/theme_extends.html.twig b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/theme_extends.html.twig
new file mode 100644 (file)
index 0000000..8c71986
--- /dev/null
@@ -0,0 +1,8 @@
+{% extends 'form_div_layout.html.twig' %}
+
+{% block form_widget_simple %}
+{% spaceless %}
+    {% set type = type|default('text') %}
+    <input type="{{ type }}" {{ block('widget_attributes') }} value="{{ value }}" rel="theme" />
+{% endspaceless %}
+{% endblock form_widget_simple %}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/theme_use.html.twig b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/theme_use.html.twig
new file mode 100644 (file)
index 0000000..d485b8d
--- /dev/null
@@ -0,0 +1,8 @@
+{% use 'form_div_layout.html.twig' %}
+
+{% block form_widget_simple %}
+{% spaceless %}
+    {% set type = type|default('text') %}
+    <input type="{{ type }}" {{ block('widget_attributes') }} value="{{ value }}" rel="theme" />
+{% endspaceless %}
+{% endblock form_widget_simple %}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php
new file mode 100644 (file)
index 0000000..90afef1
--- /dev/null
@@ -0,0 +1,85 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Tests\Node;
+
+use Symfony\Bridge\Twig\Tests\TestCase;
+use Symfony\Bridge\Twig\Node\FormThemeNode;
+
+class FormThemeTest extends TestCase
+{
+    protected function setUp()
+    {
+        parent::setUp();
+
+        if (version_compare(\Twig_Environment::VERSION, '1.5.0', '<')) {
+            $this->markTestSkipped('Requires Twig version to be at least 1.5.0.');
+        }
+    }
+
+    public function testConstructor()
+    {
+        $form = new \Twig_Node_Expression_Name('form', 0);
+        $resources = new \Twig_Node(array(
+            new \Twig_Node_Expression_Constant('tpl1', 0),
+            new \Twig_Node_Expression_Constant('tpl2', 0)
+        ));
+
+        $node = new FormThemeNode($form, $resources, 0);
+
+        $this->assertEquals($form, $node->getNode('form'));
+        $this->assertEquals($resources, $node->getNode('resources'));
+    }
+
+    public function testCompile()
+    {
+        $form = new \Twig_Node_Expression_Name('form', 0);
+        $resources = new \Twig_Node_Expression_Array(array(
+            new \Twig_Node_Expression_Constant(0, 0),
+            new \Twig_Node_Expression_Constant('tpl1', 0),
+            new \Twig_Node_Expression_Constant(1, 0),
+            new \Twig_Node_Expression_Constant('tpl2', 0)
+        ), 0);
+
+        $node = new FormThemeNode($form, $resources, 0);
+
+        $compiler = new \Twig_Compiler(new \Twig_Environment());
+
+        $this->assertEquals(
+            sprintf(
+                '$this->env->getExtension(\'form\')->renderer->setTheme(%s, array(0 => "tpl1", 1 => "tpl2"));',
+                $this->getVariableGetter('form')
+             ),
+            trim($compiler->compile($node)->getSource())
+        );
+
+        $resources = new \Twig_Node_Expression_Constant('tpl1', 0);
+
+        $node = new FormThemeNode($form, $resources, 0);
+
+        $this->assertEquals(
+            sprintf(
+                '$this->env->getExtension(\'form\')->renderer->setTheme(%s, "tpl1");',
+                $this->getVariableGetter('form')
+             ),
+            trim($compiler->compile($node)->getSource())
+        );
+    }
+
+    protected function getVariableGetter($name)
+    {
+        if (version_compare(phpversion(), '5.4.0RC1', '>=')) {
+            return sprintf('(isset($context["%s"]) ? $context["%s"] : null)', $name, $name);
+        }
+
+        return sprintf('$this->getContext($context, "%s")', $name);
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Node/SearchAndRenderBlockNodeTest.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Node/SearchAndRenderBlockNodeTest.php
new file mode 100644 (file)
index 0000000..c1f247c
--- /dev/null
@@ -0,0 +1,282 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Tests\Node;
+
+use Symfony\Bridge\Twig\Tests\TestCase;
+use Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode;
+
+class SearchAndRenderBlockNodeTest extends TestCase
+{
+    protected function setUp()
+    {
+        parent::setUp();
+
+        if (version_compare(\Twig_Environment::VERSION, '1.5.0', '<')) {
+            $this->markTestSkipped('Requires Twig version to be at least 1.5.0.');
+        }
+    }
+
+    public function testCompileWidget()
+    {
+        $arguments = new \Twig_Node(array(
+            new \Twig_Node_Expression_Name('form', 0),
+        ));
+
+        $node = new SearchAndRenderBlockNode('form_widget', $arguments, 0);
+
+        $compiler = new \Twig_Compiler(new \Twig_Environment());
+
+        $this->assertEquals(
+            sprintf(
+                '$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'widget\')',
+                $this->getVariableGetter('form')
+             ),
+            trim($compiler->compile($node)->getSource())
+        );
+    }
+
+    public function testCompileWidgetWithVariables()
+    {
+        $arguments = new \Twig_Node(array(
+            new \Twig_Node_Expression_Name('form', 0),
+            new \Twig_Node_Expression_Array(array(
+                new \Twig_Node_Expression_Constant('foo', 0),
+                new \Twig_Node_Expression_Constant('bar', 0),
+            ), 0),
+        ));
+
+        $node = new SearchAndRenderBlockNode('form_widget', $arguments, 0);
+
+        $compiler = new \Twig_Compiler(new \Twig_Environment());
+
+        $this->assertEquals(
+            sprintf(
+                '$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'widget\', array("foo" => "bar"))',
+                $this->getVariableGetter('form')
+            ),
+            trim($compiler->compile($node)->getSource())
+        );
+    }
+
+    public function testCompileLabelWithLabel()
+    {
+        $arguments = new \Twig_Node(array(
+            new \Twig_Node_Expression_Name('form', 0),
+            new \Twig_Node_Expression_Constant('my label', 0),
+        ));
+
+        $node = new SearchAndRenderBlockNode('form_label', $arguments, 0);
+
+        $compiler = new \Twig_Compiler(new \Twig_Environment());
+
+        $this->assertEquals(
+            sprintf(
+                '$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'label\', array("label" => "my label"))',
+                $this->getVariableGetter('form')
+            ),
+            trim($compiler->compile($node)->getSource())
+        );
+    }
+
+    public function testCompileLabelWithNullLabel()
+    {
+        $arguments = new \Twig_Node(array(
+            new \Twig_Node_Expression_Name('form', 0),
+            new \Twig_Node_Expression_Constant(null, 0),
+        ));
+
+        $node = new SearchAndRenderBlockNode('form_label', $arguments, 0);
+
+        $compiler = new \Twig_Compiler(new \Twig_Environment());
+
+        // "label" => null must not be included in the output!
+        // Otherwise the default label is overwritten with null.
+        $this->assertEquals(
+            sprintf(
+                '$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'label\')',
+                $this->getVariableGetter('form')
+            ),
+            trim($compiler->compile($node)->getSource())
+        );
+    }
+
+    public function testCompileLabelWithEmptyStringLabel()
+    {
+        $arguments = new \Twig_Node(array(
+            new \Twig_Node_Expression_Name('form', 0),
+            new \Twig_Node_Expression_Constant('', 0),
+        ));
+
+        $node = new SearchAndRenderBlockNode('form_label', $arguments, 0);
+
+        $compiler = new \Twig_Compiler(new \Twig_Environment());
+
+        // "label" => null must not be included in the output!
+        // Otherwise the default label is overwritten with null.
+        $this->assertEquals(
+            sprintf(
+                '$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'label\')',
+                $this->getVariableGetter('form')
+            ),
+            trim($compiler->compile($node)->getSource())
+        );
+    }
+
+    public function testCompileLabelWithDefaultLabel()
+    {
+        $arguments = new \Twig_Node(array(
+            new \Twig_Node_Expression_Name('form', 0),
+        ));
+
+        $node = new SearchAndRenderBlockNode('form_label', $arguments, 0);
+
+        $compiler = new \Twig_Compiler(new \Twig_Environment());
+
+        $this->assertEquals(
+            sprintf(
+                '$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'label\')',
+                $this->getVariableGetter('form')
+            ),
+            trim($compiler->compile($node)->getSource())
+        );
+    }
+
+    public function testCompileLabelWithAttributes()
+    {
+        $arguments = new \Twig_Node(array(
+            new \Twig_Node_Expression_Name('form', 0),
+            new \Twig_Node_Expression_Constant(null, 0),
+            new \Twig_Node_Expression_Array(array(
+                new \Twig_Node_Expression_Constant('foo', 0),
+                new \Twig_Node_Expression_Constant('bar', 0),
+            ), 0),
+        ));
+
+        $node = new SearchAndRenderBlockNode('form_label', $arguments, 0);
+
+        $compiler = new \Twig_Compiler(new \Twig_Environment());
+
+        // "label" => null must not be included in the output!
+        // Otherwise the default label is overwritten with null.
+        // https://github.com/symfony/symfony/issues/5029
+        $this->assertEquals(
+            sprintf(
+                '$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'label\', array("foo" => "bar"))',
+                $this->getVariableGetter('form')
+            ),
+            trim($compiler->compile($node)->getSource())
+        );
+    }
+
+    public function testCompileLabelWithLabelAndAttributes()
+    {
+        $arguments = new \Twig_Node(array(
+            new \Twig_Node_Expression_Name('form', 0),
+            new \Twig_Node_Expression_Constant('value in argument', 0),
+            new \Twig_Node_Expression_Array(array(
+                new \Twig_Node_Expression_Constant('foo', 0),
+                new \Twig_Node_Expression_Constant('bar', 0),
+                new \Twig_Node_Expression_Constant('label', 0),
+                new \Twig_Node_Expression_Constant('value in attributes', 0),
+            ), 0),
+        ));
+
+        $node = new SearchAndRenderBlockNode('form_label', $arguments, 0);
+
+        $compiler = new \Twig_Compiler(new \Twig_Environment());
+
+        $this->assertEquals(
+            sprintf(
+                '$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'label\', array("foo" => "bar", "label" => "value in argument"))',
+                $this->getVariableGetter('form')
+            ),
+            trim($compiler->compile($node)->getSource())
+        );
+    }
+
+    public function testCompileLabelWithLabelThatEvaluatesToNull()
+    {
+        $arguments = new \Twig_Node(array(
+            new \Twig_Node_Expression_Name('form', 0),
+            new \Twig_Node_Expression_Conditional(
+                // if
+                new \Twig_Node_Expression_Constant(true, 0),
+                // then
+                new \Twig_Node_Expression_Constant(null, 0),
+                // else
+                new \Twig_Node_Expression_Constant(null, 0),
+                0
+            ),
+        ));
+
+        $node = new SearchAndRenderBlockNode('form_label', $arguments, 0);
+
+        $compiler = new \Twig_Compiler(new \Twig_Environment());
+
+        // "label" => null must not be included in the output!
+        // Otherwise the default label is overwritten with null.
+        // https://github.com/symfony/symfony/issues/5029
+        $this->assertEquals(
+            sprintf(
+                '$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'label\', (twig_test_empty($_label_ = ((true) ? (null) : (null))) ? array() : array("label" => $_label_)))',
+                $this->getVariableGetter('form')
+            ),
+            trim($compiler->compile($node)->getSource())
+        );
+    }
+
+    public function testCompileLabelWithLabelThatEvaluatesToNullAndAttributes()
+    {
+        $arguments = new \Twig_Node(array(
+            new \Twig_Node_Expression_Name('form', 0),
+            new \Twig_Node_Expression_Conditional(
+                // if
+                new \Twig_Node_Expression_Constant(true, 0),
+                // then
+                new \Twig_Node_Expression_Constant(null, 0),
+                // else
+                new \Twig_Node_Expression_Constant(null, 0),
+                0
+            ),
+            new \Twig_Node_Expression_Array(array(
+                new \Twig_Node_Expression_Constant('foo', 0),
+                new \Twig_Node_Expression_Constant('bar', 0),
+                new \Twig_Node_Expression_Constant('label', 0),
+                new \Twig_Node_Expression_Constant('value in attributes', 0),
+            ), 0),
+        ));
+
+        $node = new SearchAndRenderBlockNode('form_label', $arguments, 0);
+
+        $compiler = new \Twig_Compiler(new \Twig_Environment());
+
+        // "label" => null must not be included in the output!
+        // Otherwise the default label is overwritten with null.
+        // https://github.com/symfony/symfony/issues/5029
+        $this->assertEquals(
+            sprintf(
+                '$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'label\', array("foo" => "bar", "label" => "value in attributes") + (twig_test_empty($_label_ = ((true) ? (null) : (null))) ? array() : array("label" => $_label_)))',
+                $this->getVariableGetter('form')
+            ),
+            trim($compiler->compile($node)->getSource())
+        );
+    }
+
+    protected function getVariableGetter($name)
+    {
+        if (version_compare(phpversion(), '5.4.0RC1', '>=')) {
+            return sprintf('(isset($context["%s"]) ? $context["%s"] : null)', $name, $name);
+        }
+
+        return sprintf('$this->getContext($context, "%s")', $name);
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/NodeVisitor/ScopeTest.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/NodeVisitor/ScopeTest.php
new file mode 100644 (file)
index 0000000..bcae591
--- /dev/null
@@ -0,0 +1,25 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Tests\NodeVisitor;
+
+use Symfony\Bridge\Twig\NodeVisitor\Scope;
+use Symfony\Bridge\Twig\Tests\TestCase;
+
+class ScopeTest extends TestCase
+{
+    public function testScopeInitiation()
+    {
+        $scope = new Scope();
+        $scope->enter();
+        $this->assertNull($scope->get('test'));
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationDefaultDomainNodeVisitorTest.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationDefaultDomainNodeVisitorTest.php
new file mode 100644 (file)
index 0000000..24a6215
--- /dev/null
@@ -0,0 +1,83 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Tests\NodeVisitor;
+
+use Symfony\Bridge\Twig\NodeVisitor\TranslationDefaultDomainNodeVisitor;
+use Symfony\Bridge\Twig\NodeVisitor\TranslationNodeVisitor;
+use Symfony\Bridge\Twig\Tests\TestCase;
+
+class TranslationDefaultDomainNodeVisitorTest extends TestCase
+{
+    private static $message = 'message';
+    private static $domain = 'domain';
+
+    /** @dataProvider getDefaultDomainAssignmentTestData */
+    public function testDefaultDomainAssignment(\Twig_Node $node)
+    {
+        $env = new \Twig_Environment(new \Twig_Loader_String(), array('cache' => false, 'autoescape' => false, 'optimizations' => 0));
+        $visitor = new TranslationDefaultDomainNodeVisitor();
+
+        // visit trans_default_domain tag
+        $defaultDomain = TwigNodeProvider::getTransDefaultDomainTag(self::$domain);
+        $visitor->enterNode($defaultDomain, $env);
+        $visitor->leaveNode($defaultDomain, $env);
+
+        // visit tested node
+        $enteredNode = $visitor->enterNode($node, $env);
+        $leavedNode = $visitor->leaveNode($node, $env);
+        $this->assertSame($node, $enteredNode);
+        $this->assertSame($node, $leavedNode);
+
+        // extracting tested node messages
+        $visitor = new TranslationNodeVisitor();
+        $visitor->enable();
+        $visitor->enterNode($node, $env);
+        $visitor->leaveNode($node, $env);
+
+        $this->assertEquals(array(array(self::$message, self::$domain)), $visitor->getMessages());
+    }
+
+    /** @dataProvider getDefaultDomainAssignmentTestData */
+    public function testNewModuleWithoutDefaultDomainTag(\Twig_Node $node)
+    {
+        $env = new \Twig_Environment(new \Twig_Loader_String(), array('cache' => false, 'autoescape' => false, 'optimizations' => 0));
+        $visitor = new TranslationDefaultDomainNodeVisitor();
+
+        // visit trans_default_domain tag
+        $newModule = TwigNodeProvider::getModule('test');
+        $visitor->enterNode($newModule, $env);
+        $visitor->leaveNode($newModule, $env);
+
+        // visit tested node
+        $enteredNode = $visitor->enterNode($node, $env);
+        $leavedNode = $visitor->leaveNode($node, $env);
+        $this->assertSame($node, $enteredNode);
+        $this->assertSame($node, $leavedNode);
+
+        // extracting tested node messages
+        $visitor = new TranslationNodeVisitor();
+        $visitor->enable();
+        $visitor->enterNode($node, $env);
+        $visitor->leaveNode($node, $env);
+
+        $this->assertEquals(array(array(self::$message, null)), $visitor->getMessages());
+    }
+
+    public function getDefaultDomainAssignmentTestData()
+    {
+        return array(
+            array(TwigNodeProvider::getTransFilter(self::$message)),
+            array(TwigNodeProvider::getTransChoiceFilter(self::$message)),
+            array(TwigNodeProvider::getTransTag(self::$message)),
+        );
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationNodeVisitorTest.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationNodeVisitorTest.php
new file mode 100644 (file)
index 0000000..4e3ee6f
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Tests\NodeVisitor;
+
+use Symfony\Bridge\Twig\NodeVisitor\TranslationNodeVisitor;
+use Symfony\Bridge\Twig\Tests\TestCase;
+
+class TranslationNodeVisitorTest extends TestCase
+{
+    /** @dataProvider getMessagesExtractionTestData */
+    public function testMessagesExtraction(\Twig_Node $node, array $expectedMessages)
+    {
+        $env = new \Twig_Environment(new \Twig_Loader_String(), array('cache' => false, 'autoescape' => false, 'optimizations' => 0));
+        $visitor = new TranslationNodeVisitor();
+        $visitor->enable();
+        $visitor->enterNode($node, $env);
+        $visitor->leaveNode($node, $env);
+        $this->assertEquals($expectedMessages, $visitor->getMessages());
+    }
+
+    public function testMessageExtractionWithInvalidDomainNode()
+    {
+        $message = 'new key';
+
+        $node = new \Twig_Node_Expression_Filter(
+            new \Twig_Node_Expression_Constant($message, 0),
+            new \Twig_Node_Expression_Constant('trans', 0),
+            new \Twig_Node(array(
+                new \Twig_Node_Expression_Array(array(), 0),
+                new \Twig_Node_Expression_Name('variable', 0),
+            )),
+            0
+        );
+
+        $this->testMessagesExtraction($node, array(array($message, TranslationNodeVisitor::UNDEFINED_DOMAIN)));
+    }
+
+    public function getMessagesExtractionTestData()
+    {
+        $message = 'new key';
+        $domain = 'domain';
+
+        return array(
+            array(TwigNodeProvider::getTransFilter($message), array(array($message, null))),
+            array(TwigNodeProvider::getTransChoiceFilter($message), array(array($message, null))),
+            array(TwigNodeProvider::getTransTag($message), array(array($message, null))),
+            array(TwigNodeProvider::getTransFilter($message, $domain), array(array($message, $domain))),
+            array(TwigNodeProvider::getTransChoiceFilter($message, $domain), array(array($message, $domain))),
+            array(TwigNodeProvider::getTransTag($message, $domain), array(array($message, $domain))),
+        );
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/NodeVisitor/TwigNodeProvider.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/NodeVisitor/TwigNodeProvider.php
new file mode 100644 (file)
index 0000000..277e777
--- /dev/null
@@ -0,0 +1,77 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Tests\NodeVisitor;
+
+use Symfony\Bridge\Twig\Node\TransDefaultDomainNode;
+use Symfony\Bridge\Twig\Node\TransNode;
+
+class TwigNodeProvider
+{
+    public static function getModule($content)
+    {
+        return new \Twig_Node_Module(
+            new \Twig_Node_Expression_Constant($content, 0),
+            null,
+            new \Twig_Node_Expression_Array(array(), 0),
+            new \Twig_Node_Expression_Array(array(), 0),
+            new \Twig_Node_Expression_Array(array(), 0),
+            null,
+            null
+        );
+    }
+
+    public static function getTransFilter($message, $domain = null)
+    {
+        $arguments = $domain ? array(
+            new \Twig_Node_Expression_Array(array(), 0),
+            new \Twig_Node_Expression_Constant($domain, 0),
+        ) : array();
+
+        return new \Twig_Node_Expression_Filter(
+            new \Twig_Node_Expression_Constant($message, 0),
+            new \Twig_Node_Expression_Constant('trans', 0),
+            new \Twig_Node($arguments),
+            0
+        );
+    }
+
+    public static function getTransChoiceFilter($message, $domain = null)
+    {
+        $arguments = $domain ? array(
+            new \Twig_Node_Expression_Constant(0, 0),
+            new \Twig_Node_Expression_Array(array(), 0),
+            new \Twig_Node_Expression_Constant($domain, 0),
+        ) : array();
+
+        return new \Twig_Node_Expression_Filter(
+            new \Twig_Node_Expression_Constant($message, 0),
+            new \Twig_Node_Expression_Constant('transchoice', 0),
+            new \Twig_Node($arguments),
+            0
+        );
+    }
+
+    public static function getTransTag($message, $domain = null)
+    {
+        return new TransNode(
+            new \Twig_Node_Body(array(), array('data' => $message)),
+            $domain ? new \Twig_Node_Expression_Constant($domain, 0) : null
+        );
+    }
+
+    public static function getTransDefaultDomainTag($domain)
+    {
+        return new TransDefaultDomainNode(
+            new \Twig_Node_Expression_Constant($domain, 0)
+        );
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/TestCase.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/TestCase.php
new file mode 100644 (file)
index 0000000..ecfb7da
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Tests;
+
+abstract class TestCase extends \PHPUnit_Framework_TestCase
+{
+    protected function setUp()
+    {
+        if (!class_exists('Twig_Environment')) {
+            $this->markTestSkipped('Twig is not available.');
+        }
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php
new file mode 100644 (file)
index 0000000..077cd76
--- /dev/null
@@ -0,0 +1,108 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Tests\Node;
+
+use Symfony\Bridge\Twig\Tests\TestCase;
+use Symfony\Bridge\Twig\TokenParser\FormThemeTokenParser;
+use Symfony\Bridge\Twig\Node\FormThemeNode;
+
+class FormThemeTokenParserTest extends TestCase
+{
+    protected function setUp()
+    {
+        parent::setUp();
+
+        if (version_compare(\Twig_Environment::VERSION, '1.5.0', '<')) {
+            $this->markTestSkipped('Requires Twig version to be at least 1.5.0.');
+        }
+    }
+
+    /**
+     * @dataProvider getTestsForFormTheme
+     */
+    public function testCompile($source, $expected)
+    {
+        $env = new \Twig_Environment(new \Twig_Loader_String(), array('cache' => false, 'autoescape' => false, 'optimizations' => 0));
+        $env->addTokenParser(new FormThemeTokenParser());
+        $stream = $env->tokenize($source);
+        $parser = new \Twig_Parser($env);
+
+        $this->assertEquals($expected, $parser->parse($stream)->getNode('body')->getNode(0));
+    }
+
+    public function getTestsForFormTheme()
+    {
+        return array(
+            array(
+                '{% form_theme form "tpl1" %}',
+                new FormThemeNode(
+                    new \Twig_Node_Expression_Name('form', 1),
+                    new \Twig_Node_Expression_Array(array(
+                        new \Twig_Node_Expression_Constant(0, 1),
+                        new \Twig_Node_Expression_Constant('tpl1', 1),
+                    ), 1),
+                    1,
+                    'form_theme'
+                )
+            ),
+            array(
+                '{% form_theme form "tpl1" "tpl2" %}',
+                new FormThemeNode(
+                    new \Twig_Node_Expression_Name('form', 1),
+                    new \Twig_Node_Expression_Array(array(
+                        new \Twig_Node_Expression_Constant(0, 1),
+                        new \Twig_Node_Expression_Constant('tpl1', 1),
+                        new \Twig_Node_Expression_Constant(1, 1),
+                        new \Twig_Node_Expression_Constant('tpl2', 1)
+                    ), 1),
+                    1,
+                    'form_theme'
+                )
+            ),
+            array(
+                '{% form_theme form with "tpl1" %}',
+                new FormThemeNode(
+                    new \Twig_Node_Expression_Name('form', 1),
+                    new \Twig_Node_Expression_Constant('tpl1', 1),
+                    1,
+                    'form_theme'
+                )
+            ),
+            array(
+                '{% form_theme form with ["tpl1"] %}',
+                new FormThemeNode(
+                    new \Twig_Node_Expression_Name('form', 1),
+                    new \Twig_Node_Expression_Array(array(
+                        new \Twig_Node_Expression_Constant(0, 1),
+                        new \Twig_Node_Expression_Constant('tpl1', 1),
+                    ), 1),
+                    1,
+                    'form_theme'
+                )
+            ),
+            array(
+                '{% form_theme form with ["tpl1", "tpl2"] %}',
+                new FormThemeNode(
+                    new \Twig_Node_Expression_Name('form', 1),
+                    new \Twig_Node_Expression_Array(array(
+                        new \Twig_Node_Expression_Constant(0, 1),
+                        new \Twig_Node_Expression_Constant('tpl1', 1),
+                        new \Twig_Node_Expression_Constant(1, 1),
+                        new \Twig_Node_Expression_Constant('tpl2', 1)
+                    ), 1),
+                    1,
+                    'form_theme'
+                )
+            ),
+        );
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php
new file mode 100644 (file)
index 0000000..a2c5cd3
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Tests\Translation;
+
+use Symfony\Bridge\Twig\Extension\TranslationExtension;
+use Symfony\Bridge\Twig\Translation\TwigExtractor;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Bridge\Twig\Tests\TestCase;
+
+class TwigExtractorTest extends TestCase
+{
+    protected function setUp()
+    {
+        if (!class_exists('Symfony\Component\Translation\Translator')) {
+            $this->markTestSkipped('The "Translation" component is not available');
+        }
+    }
+
+    /**
+     * @dataProvider getExtractData
+     */
+    public function testExtract($template, $messages)
+    {
+        $loader = new \Twig_Loader_Array(array());
+        $twig = new \Twig_Environment($loader, array(
+            'strict_variables' => true,
+            'debug' => true,
+            'cache' => false,
+            'autoescape' => false,
+        ));
+        $twig->addExtension(new TranslationExtension($this->getMock('Symfony\Component\Translation\TranslatorInterface')));
+
+        $extractor = new TwigExtractor($twig);
+        $extractor->setPrefix('prefix');
+        $catalogue = new MessageCatalogue('en');
+
+        $m = new \ReflectionMethod($extractor, 'extractTemplate');
+        $m->setAccessible(true);
+        $m->invoke($extractor, $template, $catalogue);
+
+        foreach ($messages as $key => $domain) {
+            $this->assertTrue($catalogue->has($key, $domain));
+            $this->assertEquals('prefix'.$key, $catalogue->get($key, $domain));
+        }
+    }
+
+    public function getExtractData()
+    {
+        return array(
+            array('{{ "new key" | trans() }}', array('new key' => 'messages')),
+            array('{{ "new key" | trans() | upper }}', array('new key' => 'messages')),
+            array('{{ "new key" | trans({}, "domain") }}', array('new key' => 'domain')),
+            array('{{ "new key" | transchoice(1) }}', array('new key' => 'messages')),
+            array('{{ "new key" | transchoice(1) | upper }}', array('new key' => 'messages')),
+            array('{{ "new key" | transchoice(1, {}, "domain") }}', array('new key' => 'domain')),
+            array('{% trans %}new key{% endtrans %}', array('new key' => 'messages')),
+            array('{% trans %}  new key  {% endtrans %}', array('new key' => 'messages')),
+            array('{% trans from "domain" %}new key{% endtrans %}', array('new key' => 'domain')),
+            array('{% set foo = "new key" | trans %}', array('new key' => 'messages')),
+            array('{{ 1 ? "new key" | trans : "another key" | trans }}', array('new key' => 'messages', 'another key' => 'messages')),
+
+            // make sure 'trans_default_domain' tag is supported
+            array('{% trans_default_domain "domain" %}{{ "new key"|trans }}', array('new key' => 'domain')),
+            array('{% trans_default_domain "domain" %}{{ "new key"|transchoice }}', array('new key' => 'domain')),
+            array('{% trans_default_domain "domain" %}{% trans %}new key{% endtrans %}', array('new key' => 'domain')),
+
+            // make sure this works with twig's named arguments
+            array('{{ "new key" | trans(domain="domain") }}', array('new key' => 'domain')),
+            array('{{ "new key" | transchoice(domain="domain", count=1) }}', array('new key' => 'domain')),
+        );
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/TokenParser/FormThemeTokenParser.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/TokenParser/FormThemeTokenParser.php
new file mode 100644 (file)
index 0000000..244d676
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\TokenParser;
+
+use Symfony\Bridge\Twig\Node\FormThemeNode;
+
+/**
+ * Token Parser for the 'form_theme' tag.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class FormThemeTokenParser extends \Twig_TokenParser
+{
+    /**
+     * Parses a token and returns a node.
+     *
+     * @param \Twig_Token $token A Twig_Token instance
+     *
+     * @return \Twig_NodeInterface A Twig_NodeInterface instance
+     */
+    public function parse(\Twig_Token $token)
+    {
+        $lineno = $token->getLine();
+        $stream = $this->parser->getStream();
+
+        $form = $this->parser->getExpressionParser()->parseExpression();
+
+        if ($this->parser->getStream()->test(\Twig_Token::NAME_TYPE, 'with')) {
+            $this->parser->getStream()->next();
+            $resources = $this->parser->getExpressionParser()->parseExpression();
+        } else {
+            $resources = new \Twig_Node_Expression_Array(array(), $stream->getCurrent()->getLine());
+            do {
+                $resources->addElement($this->parser->getExpressionParser()->parseExpression());
+            } while (!$stream->test(\Twig_Token::BLOCK_END_TYPE));
+        }
+
+        $stream->expect(\Twig_Token::BLOCK_END_TYPE);
+
+        return new FormThemeNode($form, $resources, $lineno, $this->getTag());
+    }
+
+    /**
+     * Gets the tag name associated with this token parser.
+     *
+     * @return string The tag name
+     */
+    public function getTag()
+    {
+        return 'form_theme';
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/TokenParser/TransChoiceTokenParser.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/TokenParser/TransChoiceTokenParser.php
new file mode 100644 (file)
index 0000000..be8ac5c
--- /dev/null
@@ -0,0 +1,89 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\TokenParser;
+
+use Symfony\Bridge\Twig\Node\TransNode;
+
+/**
+ * Token Parser for the 'transchoice' tag.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class TransChoiceTokenParser extends TransTokenParser
+{
+    /**
+     * Parses a token and returns a node.
+     *
+     * @param \Twig_Token $token A Twig_Token instance
+     *
+     * @return \Twig_NodeInterface A Twig_NodeInterface instance
+     *
+     * @throws \Twig_Error_Syntax
+     */
+    public function parse(\Twig_Token $token)
+    {
+        $lineno = $token->getLine();
+        $stream = $this->parser->getStream();
+
+        $vars = new \Twig_Node_Expression_Array(array(), $lineno);
+
+        $count = $this->parser->getExpressionParser()->parseExpression();
+
+        $domain = null;
+        $locale = null;
+
+        if ($stream->test('with')) {
+            // {% transchoice count with vars %}
+            $stream->next();
+            $vars = $this->parser->getExpressionParser()->parseExpression();
+        }
+
+        if ($stream->test('from')) {
+            // {% transchoice count from "messages" %}
+            $stream->next();
+            $domain = $this->parser->getExpressionParser()->parseExpression();
+        }
+
+        if ($stream->test('into')) {
+            // {% transchoice count into "fr" %}
+            $stream->next();
+            $locale =  $this->parser->getExpressionParser()->parseExpression();
+        }
+
+        $stream->expect(\Twig_Token::BLOCK_END_TYPE);
+
+        $body = $this->parser->subparse(array($this, 'decideTransChoiceFork'), true);
+
+        if (!$body instanceof \Twig_Node_Text && !$body instanceof \Twig_Node_Expression) {
+            throw new \Twig_Error_Syntax('A message must be a simple text.');
+        }
+
+        $stream->expect(\Twig_Token::BLOCK_END_TYPE);
+
+        return new TransNode($body, $domain, $count, $vars, $locale, $lineno, $this->getTag());
+    }
+
+    public function decideTransChoiceFork($token)
+    {
+        return $token->test(array('endtranschoice'));
+    }
+
+    /**
+     * Gets the tag name associated with this token parser.
+     *
+     * @return string The tag name
+     */
+    public function getTag()
+    {
+        return 'transchoice';
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/TokenParser/TransDefaultDomainTokenParser.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/TokenParser/TransDefaultDomainTokenParser.php
new file mode 100644 (file)
index 0000000..0a0ed55
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\TokenParser;
+
+use Symfony\Bridge\Twig\Node\TransDefaultDomainNode;
+
+/**
+ * Token Parser for the 'trans_default_domain' tag.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class TransDefaultDomainTokenParser extends \Twig_TokenParser
+{
+    /**
+     * Parses a token and returns a node.
+     *
+     * @param \Twig_Token $token A Twig_Token instance
+     *
+     * @return \Twig_NodeInterface A Twig_NodeInterface instance
+     */
+    public function parse(\Twig_Token $token)
+    {
+        $expr = $this->parser->getExpressionParser()->parseExpression();
+
+        $this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE);
+
+        return new TransDefaultDomainNode($expr, $token->getLine(), $this->getTag());
+    }
+
+    /**
+     * Gets the tag name associated with this token parser.
+     *
+     * @return string The tag name
+     */
+    public function getTag()
+    {
+        return 'trans_default_domain';
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/TokenParser/TransTokenParser.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/TokenParser/TransTokenParser.php
new file mode 100644 (file)
index 0000000..a11681c
--- /dev/null
@@ -0,0 +1,89 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\TokenParser;
+
+use Symfony\Bridge\Twig\Node\TransNode;
+
+/**
+ * Token Parser for the 'trans' tag.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class TransTokenParser extends \Twig_TokenParser
+{
+    /**
+     * Parses a token and returns a node.
+     *
+     * @param \Twig_Token $token A Twig_Token instance
+     *
+     * @return \Twig_NodeInterface A Twig_NodeInterface instance
+     *
+     * @throws \Twig_Error_Syntax
+     */
+    public function parse(\Twig_Token $token)
+    {
+        $lineno = $token->getLine();
+        $stream = $this->parser->getStream();
+
+        $vars = new \Twig_Node_Expression_Array(array(), $lineno);
+        $domain = null;
+        $locale = null;
+        if (!$stream->test(\Twig_Token::BLOCK_END_TYPE)) {
+            if ($stream->test('with')) {
+                // {% trans with vars %}
+                $stream->next();
+                $vars = $this->parser->getExpressionParser()->parseExpression();
+            }
+
+            if ($stream->test('from')) {
+                // {% trans from "messages" %}
+                $stream->next();
+                $domain = $this->parser->getExpressionParser()->parseExpression();
+            }
+
+            if ($stream->test('into')) {
+                // {% trans into "fr" %}
+                $stream->next();
+                $locale =  $this->parser->getExpressionParser()->parseExpression();
+            } elseif (!$stream->test(\Twig_Token::BLOCK_END_TYPE)) {
+                throw new \Twig_Error_Syntax('Unexpected token. Twig was looking for the "with" or "from" keyword.');
+            }
+        }
+
+        // {% trans %}message{% endtrans %}
+        $stream->expect(\Twig_Token::BLOCK_END_TYPE);
+        $body = $this->parser->subparse(array($this, 'decideTransFork'), true);
+
+        if (!$body instanceof \Twig_Node_Text && !$body instanceof \Twig_Node_Expression) {
+            throw new \Twig_Error_Syntax('A message inside a trans tag must be a simple text');
+        }
+
+        $stream->expect(\Twig_Token::BLOCK_END_TYPE);
+
+        return new TransNode($body, $domain, null, $vars, $locale, $lineno, $this->getTag());
+    }
+
+    public function decideTransFork($token)
+    {
+        return $token->test(array('endtrans'));
+    }
+
+    /**
+     * Gets the tag name associated with this token parser.
+     *
+     * @return string The tag name
+     */
+    public function getTag()
+    {
+        return 'trans';
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Translation/TwigExtractor.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Translation/TwigExtractor.php
new file mode 100644 (file)
index 0000000..b93193f
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Translation;
+
+use Symfony\Component\Finder\Finder;
+use Symfony\Component\Translation\Extractor\ExtractorInterface;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * TwigExtractor extracts translation messages from a twig template.
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class TwigExtractor implements ExtractorInterface
+{
+    /**
+     * Default domain for found messages.
+     *
+     * @var string
+     */
+    private $defaultDomain = 'messages';
+
+    /**
+     * Prefix for found message.
+     *
+     * @var string
+     */
+    private $prefix = '';
+
+    /**
+     * The twig environment.
+     *
+     * @var \Twig_Environment
+     */
+    private $twig;
+
+    public function __construct(\Twig_Environment $twig)
+    {
+        $this->twig = $twig;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function extract($directory, MessageCatalogue $catalogue)
+    {
+        // load any existing translation files
+        $finder = new Finder();
+        $files = $finder->files()->name('*.twig')->in($directory);
+        foreach ($files as $file) {
+            $this->extractTemplate(file_get_contents($file->getPathname()), $catalogue);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function setPrefix($prefix)
+    {
+        $this->prefix = $prefix;
+    }
+
+    protected function extractTemplate($template, MessageCatalogue $catalogue)
+    {
+        $visitor = $this->twig->getExtension('translator')->getTranslationNodeVisitor();
+        $visitor->enable();
+
+        $this->twig->parse($this->twig->tokenize($template));
+
+        foreach ($visitor->getMessages() as $message) {
+            $catalogue->set(trim($message[0]), $this->prefix.trim($message[0]), $message[1] ? $message[1] : $this->defaultDomain);
+        }
+
+        $visitor->disable();
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/TwigEngine.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/TwigEngine.php
new file mode 100644 (file)
index 0000000..955d4e0
--- /dev/null
@@ -0,0 +1,126 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig;
+
+use Symfony\Component\Templating\EngineInterface;
+use Symfony\Component\Templating\StreamingEngineInterface;
+use Symfony\Component\Templating\TemplateNameParserInterface;
+
+/**
+ * This engine knows how to render Twig templates.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class TwigEngine implements EngineInterface, StreamingEngineInterface
+{
+    protected $environment;
+    protected $parser;
+
+    /**
+     * Constructor.
+     *
+     * @param \Twig_Environment           $environment A \Twig_Environment instance
+     * @param TemplateNameParserInterface $parser      A TemplateNameParserInterface instance
+     */
+    public function __construct(\Twig_Environment $environment, TemplateNameParserInterface $parser)
+    {
+        $this->environment = $environment;
+        $this->parser = $parser;
+    }
+
+    /**
+     * Renders a template.
+     *
+     * @param mixed $name       A template name
+     * @param array $parameters An array of parameters to pass to the template
+     *
+     * @return string The evaluated template as a string
+     *
+     * @throws \InvalidArgumentException if the template does not exist
+     * @throws \RuntimeException         if the template cannot be rendered
+     */
+    public function render($name, array $parameters = array())
+    {
+        return $this->load($name)->render($parameters);
+    }
+
+    /**
+     * Streams a template.
+     *
+     * @param mixed $name       A template name or a TemplateReferenceInterface instance
+     * @param array $parameters An array of parameters to pass to the template
+     *
+     * @throws \RuntimeException if the template cannot be rendered
+     */
+    public function stream($name, array $parameters = array())
+    {
+        $this->load($name)->display($parameters);
+    }
+
+    /**
+     * Returns true if the template exists.
+     *
+     * @param mixed $name A template name
+     *
+     * @return Boolean true if the template exists, false otherwise
+     */
+    public function exists($name)
+    {
+        try {
+            $this->load($name);
+        } catch (\InvalidArgumentException $e) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns true if this class is able to render the given template.
+     *
+     * @param string $name A template name
+     *
+     * @return Boolean True if this class supports the given resource, false otherwise
+     */
+    public function supports($name)
+    {
+        if ($name instanceof \Twig_Template) {
+            return true;
+        }
+
+        $template = $this->parser->parse($name);
+
+        return 'twig' === $template->get('engine');
+    }
+
+    /**
+     * Loads the given template.
+     *
+     * @param mixed $name A template name or an instance of Twig_Template
+     *
+     * @return \Twig_TemplateInterface A \Twig_TemplateInterface instance
+     *
+     * @throws \InvalidArgumentException if the template does not exist
+     */
+    protected function load($name)
+    {
+        if ($name instanceof \Twig_Template) {
+            return $name;
+        }
+
+        try {
+            return $this->environment->loadTemplate($name);
+        } catch (\Twig_Error_Loader $e) {
+            throw new \InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
+        }
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/composer.json b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/composer.json
new file mode 100644 (file)
index 0000000..9cd57ae
--- /dev/null
@@ -0,0 +1,50 @@
+{
+    "name": "symfony/twig-bridge",
+    "type": "symfony-bridge",
+    "description": "Symfony Twig Bridge",
+    "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",
+        "twig/twig": "~1.11"
+    },
+    "require-dev": {
+        "symfony/form": "2.2.*",
+        "symfony/http-kernel": "~2.2",
+        "symfony/routing": "~2.2",
+        "symfony/templating": "~2.1",
+        "symfony/translation": "~2.2",
+        "symfony/yaml": "~2.0",
+        "symfony/security": "~2.0"
+    },
+    "suggest": {
+        "symfony/form": "",
+        "symfony/http-kernel": "",
+        "symfony/routing": "",
+        "symfony/templating": "",
+        "symfony/translation": "",
+        "symfony/yaml": "",
+        "symfony/security": ""
+    },
+    "autoload": {
+        "psr-0": { "Symfony\\Bridge\\Twig\\": "" }
+    },
+    "target-dir": "Symfony/Bridge/Twig",
+    "minimum-stability": "dev",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "2.3-dev"
+        }
+    }
+}
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/phpunit.xml.dist b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/phpunit.xml.dist
new file mode 100644 (file)
index 0000000..cc9e0e8
--- /dev/null
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         processIsolation="false"
+         stopOnFailure="false"
+         syntaxCheck="false"
+         bootstrap="vendor/autoload.php"
+>
+    <testsuites>
+        <testsuite name="Symfony Twig Bridge Test Suite">
+            <directory>./Tests/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./</directory>
+            <exclude>
+                <directory>./Resources</directory>
+                <directory>./Tests</directory>
+                <directory>./vendor</directory>
+            </exclude>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/vendor/twig/extensions b/vendor/twig/extensions
new file mode 160000 (submodule)
index 0000000..f5b0c84
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit f5b0c84f3699e494c84ee627d7d583e115d2c4a2
diff --git a/vendor/twig/twig/.editorconfig b/vendor/twig/twig/.editorconfig
new file mode 100644 (file)
index 0000000..270f1d1
--- /dev/null
@@ -0,0 +1,18 @@
+; top-most EditorConfig file
+root = true
+
+; Unix-style newlines
+[*]
+end_of_line = LF
+
+[*.php]
+indent_style = space
+indent_size = 4
+
+[*.test]
+indent_style = space
+indent_size = 4
+
+[*.rst]
+indent_style = space
+indent_size = 4
diff --git a/vendor/twig/twig/.gitignore b/vendor/twig/twig/.gitignore
new file mode 100644 (file)
index 0000000..840b78e
--- /dev/null
@@ -0,0 +1,2 @@
+/ext/twig/autom4te.cache/
+
diff --git a/vendor/twig/twig/.travis.yml b/vendor/twig/twig/.travis.yml
new file mode 100644 (file)
index 0000000..8569a39
--- /dev/null
@@ -0,0 +1,15 @@
+language: php
+
+php:
+  - 5.2
+  - 5.3
+  - 5.4
+  - 5.5
+
+env:
+  - TWIG_EXT=no
+  - TWIG_EXT=yes
+
+before_script:
+  - if [ "$TWIG_EXT" == "yes" ]; then sh -c "cd ext/twig && phpize && ./configure --enable-twig && make && sudo make install"; fi
+  - if [ "$TWIG_EXT" == "yes" ]; then echo "extension=twig.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`; fi
diff --git a/vendor/twig/twig/AUTHORS b/vendor/twig/twig/AUTHORS
new file mode 100644 (file)
index 0000000..eb5db05
--- /dev/null
@@ -0,0 +1,9 @@
+Twig is written and maintained by the Twig Team:
+
+Lead Developer:
+
+- Fabien Potencier <fabien.potencier@symfony-project.org>
+
+Project Founder:
+
+- Armin Ronacher <armin.ronacher@active-4.com>
diff --git a/vendor/twig/twig/CHANGELOG b/vendor/twig/twig/CHANGELOG
new file mode 100644 (file)
index 0000000..80ff9d2
--- /dev/null
@@ -0,0 +1,637 @@
+* 1.13.2 (2013-08-03)
+
+ * fixed the error line number for an error occurs in and embedded template
+ * fixed crashes of the C extension on some edge cases
+
+* 1.13.1 (2013-06-06)
+
+ * added the possibility to ignore the filesystem constructor argument in Twig_Loader_Filesystem
+ * fixed Twig_Loader_Chain::exists() for a loader which implements Twig_ExistsLoaderInterface
+ * adjusted backtrace call to reduce memory usage when an error occurs
+ * added support for object instances as the second argument of the constant test
+ * fixed the include function when used in an assignment
+
+* 1.13.0 (2013-05-10)
+
+ * fixed getting a numeric-like item on a variable ('09' for instance)
+ * fixed getting a boolean or float key on an array, so it is consistent with PHP's array access:
+   `{{ array[false] }}` behaves the same as `echo $array[false];` (equals `$array[0]`)
+ * made the escape filter 20% faster for happy path (escaping string for html with UTF-8)
+ * changed ☃ to § in tests
+ * enforced usage of named arguments after positional ones
+
+* 1.12.3 (2013-04-08)
+
+ * fixed a security issue in the filesystem loader where it was possible to include a template one
+   level above the configured path
+ * fixed fatal error that should be an exception when adding a filter/function/test too late
+ * added a batch filter
+ * added support for encoding an array as query string in the url_encode filter
+
+* 1.12.2 (2013-02-09)
+
+ * fixed the timezone used by the date filter and function when the given date contains a timezone (like 2010-01-28T15:00:00+02:00)
+ * fixed globals when getGlobals is called early on
+ * added the first and last filter
+
+* 1.12.1 (2013-01-15)
+
+ * added support for object instances as the second argument of the constant function
+ * relaxed globals management to avoid a BC break
+ * added support for {{ some_string[:2] }}
+
+* 1.12.0 (2013-01-08)
+
+ * added verbatim as an alias for the raw tag to avoid confusion with the raw filter
+ * fixed registration of tests and functions as anonymous functions
+ * fixed globals management
+
+* 1.12.0-RC1 (2012-12-29)
+
+ * added an include function (does the same as the include tag but in a more flexible way)
+ * added the ability to use any PHP callable to define filters, functions, and tests
+ * added a syntax error when using a loop variable that is not defined
+ * added the ability to set default values for macro arguments
+ * added support for named arguments for filters, tests, and functions
+ * moved filters/functions/tests syntax errors to the parser
+ * added support for extended ternary operator syntaxes
+
+* 1.11.1 (2012-11-11)
+
+ * fixed debug info line numbering (was off by 2)
+ * fixed escaping when calling a macro inside another one (regression introduced in 1.9.1)
+ * optimized variable access on PHP 5.4
+ * fixed a crash of the C extension when an exception was thrown from a macro called without being imported (using _self.XXX)
+
+* 1.11.0 (2012-11-07)
+
+ * fixed macro compilation when a variable name is a PHP reserved keyword
+ * changed the date filter behavior to always apply the default timezone, except if false is passed as the timezone
+ * fixed bitwise operator precedences
+ * added the template_from_string function
+ * fixed default timezone usage for the date function
+ * optimized the way Twig exceptions are managed (to make them faster)
+ * added Twig_ExistsLoaderInterface (implementing this interface in your loader make the chain loader much faster)
+
+* 1.10.3 (2012-10-19)
+
+ * fixed wrong template location in some error messages
+ * reverted a BC break introduced in 1.10.2
+ * added a split filter
+
+* 1.10.2 (2012-10-15)
+
+ * fixed macro calls on PHP 5.4
+
+* 1.10.1 (2012-10-15)
+
+ * made a speed optimization to macro calls when imported via the "import" tag
+ * fixed C extension compilation on Windows
+ * fixed a segfault in the C extension when using DateTime objects
+
+* 1.10.0 (2012-09-28)
+
+ * extracted functional tests framework to make it reusable for third-party extensions
+ * added namespaced templates support in Twig_Loader_Filesystem
+ * added Twig_Loader_Filesystem::prependPath()
+ * fixed an error when a token parser pass a closure as a test to the subparse() method
+
+* 1.9.2 (2012-08-25)
+
+ * fixed the in operator for objects that contain circular references
+ * fixed the C extension when accessing a public property of an object implementing the \ArrayAccess interface
+
+* 1.9.1 (2012-07-22)
+
+ * optimized macro calls when auto-escaping is on
+ * fixed wrong parent class for Twig_Function_Node
+ * made Twig_Loader_Chain more explicit about problems
+
+* 1.9.0 (2012-07-13)
+
+ * made the parsing independent of the template loaders
+ * fixed exception trace when an error occurs when rendering a child template
+ * added escaping strategies for CSS, URL, and HTML attributes
+ * fixed nested embed tag calls
+ * added the date_modify filter
+
+* 1.8.3 (2012-06-17)
+
+ * fixed paths in the filesystem loader when passing a path that ends with a slash or a backslash
+ * fixed escaping when a project defines a function named html or js
+ * fixed chmod mode to apply the umask correctly
+
+* 1.8.2 (2012-05-30)
+
+ * added the abs filter
+ * fixed a regression when using a number in template attributes
+ * fixed compiler when mbstring.func_overload is set to 2
+ * fixed DateTimeZone support in date filter
+
+* 1.8.1 (2012-05-17)
+
+ * fixed a regression when dealing with SimpleXMLElement instances in templates
+ * fixed "is_safe" value for the "dump" function when "html_errors" is not defined in php.ini
+ * switched to use mbstring whenever possible instead of iconv (you might need to update your encoding as mbstring and iconv encoding names sometimes differ)
+
+* 1.8.0 (2012-05-08)
+
+ * enforced interface when adding tests, filters, functions, and node visitors from extensions
+ * fixed a side-effect of the date filter where the timezone might be changed
+ * simplified usage of the autoescape tag; the only (optional) argument is now the escaping strategy or false (with a BC layer)
+ * added a way to dynamically change the auto-escaping strategy according to the template "filename"
+ * changed the autoescape option to also accept a supported escaping strategy (for BC, true is equivalent to html)
+ * added an embed tag
+
+* 1.7.0 (2012-04-24)
+
+ * fixed a PHP warning when using CIFS
+ * fixed template line number in some exceptions
+ * added an iterable test
+ * added an error when defining two blocks with the same name in a template
+ * added the preserves_safety option for filters
+ * fixed a PHP notice when trying to access a key on a non-object/array variable
+ * enhanced error reporting when the template file is an instance of SplFileInfo
+ * added Twig_Environment::mergeGlobals()
+ * added compilation checks to avoid misuses of the sandbox tag
+ * fixed filesystem loader freshness logic for high traffic websites
+ * fixed random function when charset is null
+
+* 1.6.5 (2012-04-11)
+
+ * fixed a regression when a template only extends another one without defining any blocks
+
+* 1.6.4 (2012-04-02)
+
+ * fixed PHP notice in Twig_Error::guessTemplateLine() introduced in 1.6.3
+ * fixed performance when compiling large files
+ * optimized parent template creation when the template does not use dynamic inheritance
+
+* 1.6.3 (2012-03-22)
+
+ * fixed usage of Z_ADDREF_P for PHP 5.2 in the C extension
+ * fixed compilation of numeric values used in templates when using a locale where the decimal separator is not a dot
+ * made the strategy used to guess the real template file name and line number in exception messages much faster and more accurate
+
+* 1.6.2 (2012-03-18)
+
+ * fixed sandbox mode when used with inheritance
+ * added preserveKeys support for the slice filter
+ * fixed the date filter when a DateTime instance is passed with a specific timezone
+ * added a trim filter
+
+* 1.6.1 (2012-02-29)
+
+ * fixed Twig C extension
+ * removed the creation of Twig_Markup instances when not needed
+ * added a way to set the default global timezone for dates
+ * fixed the slice filter on strings when the length is not specified
+ * fixed the creation of the cache directory in case of a race condition
+
+* 1.6.0 (2012-02-04)
+
+ * fixed raw blocks when used with the whitespace trim option
+ * made a speed optimization to macro calls when imported via the "from" tag
+ * fixed globals, parsers, visitors, filters, tests, and functions management in Twig_Environment when a new one or new extension is added
+ * fixed the attribute function when passing arguments
+ * added slice notation support for the [] operator (syntactic sugar for the slice operator)
+ * added a slice filter
+ * added string support for the reverse filter
+ * fixed the empty test and the length filter for Twig_Markup instances
+ * added a date function to ease date comparison
+ * fixed unary operators precedence
+ * added recursive parsing support in the parser
+ * added string and integer handling for the random function
+
+* 1.5.1 (2012-01-05)
+
+ * fixed a regression when parsing strings
+
+* 1.5.0 (2012-01-04)
+
+ * added Traversable objects support for the join filter
+
+* 1.5.0-RC2 (2011-12-30)
+
+ * added a way to set the default global date interval format
+ * fixed the date filter for DateInterval instances (setTimezone() does not exist for them)
+ * refactored Twig_Template::display() to ease its extension
+ * added a number_format filter
+
+* 1.5.0-RC1 (2011-12-26)
+
+ * removed the need to quote hash keys
+ * allowed hash keys to be any expression
+ * added a do tag
+ * added a flush tag
+ * added support for dynamically named filters and functions
+ * added a dump function to help debugging templates
+ * added a nl2br filter
+ * added a random function
+ * added a way to change the default format for the date filter
+ * fixed the lexer when an operator ending with a letter ends a line
+ * added string interpolation support
+ * enhanced exceptions for unknown filters, functions, tests, and tags
+
+* 1.4.0 (2011-12-07)
+
+ * fixed lexer when using big numbers (> PHP_INT_MAX)
+ * added missing preserveKeys argument to the reverse filter
+ * fixed macros containing filter tag calls
+
+* 1.4.0-RC2 (2011-11-27)
+
+ * removed usage of Reflection in Twig_Template::getAttribute()
+ * added a C extension that can optionally replace Twig_Template::getAttribute()
+ * added negative timestamp support to the date filter
+
+* 1.4.0-RC1 (2011-11-20)
+
+ * optimized variable access when using PHP 5.4
+ * changed the precedence of the .. operator to be more consistent with languages that implements such a feature like Ruby
+ * added an Exception to Twig_Loader_Array::isFresh() method when the template does not exist to be consistent with other loaders
+ * added Twig_Function_Node to allow more complex functions to have their own Node class
+ * added Twig_Filter_Node to allow more complex filters to have their own Node class
+ * added Twig_Test_Node to allow more complex tests to have their own Node class
+ * added a better error message when a template is empty but contain a BOM
+ * fixed "in" operator for empty strings
+ * fixed the "defined" test and the "default" filter (now works with more than one call (foo.bar.foo) and for both values of the strict_variables option)
+ * changed the way extensions are loaded (addFilter/addFunction/addGlobal/addTest/addNodeVisitor/addTokenParser/addExtension can now be called in any order)
+ * added Twig_Environment::display()
+ * made the escape filter smarter when the encoding is not supported by PHP
+ * added a convert_encoding filter
+ * moved all node manipulations outside the compile() Node method
+ * made several speed optimizations
+
+* 1.3.0 (2011-10-08)
+
+no changes
+
+* 1.3.0-RC1 (2011-10-04)
+
+ * added an optimization for the parent() function
+ * added cache reloading when auto_reload is true and an extension has been modified
+ * added the possibility to force the escaping of a string already marked as safe (instance of Twig_Markup)
+ * allowed empty templates to be used as traits
+ * added traits support for the "parent" function
+
+* 1.2.0 (2011-09-13)
+
+no changes
+
+* 1.2.0-RC1 (2011-09-10)
+
+ * enhanced the exception when a tag remains unclosed
+ * added support for empty Countable objects for the "empty" test
+ * fixed algorithm that determines if a template using inheritance is valid (no output between block definitions)
+ * added better support for encoding problems when escaping a string (available as of PHP 5.4)
+ * added a way to ignore a missing template when using the "include" tag ({% include "foo" ignore missing %})
+ * added support for an array of templates to the "include" and "extends" tags ({% include ['foo', 'bar'] %})
+ * added support for bitwise operators in expressions
+ * added the "attribute" function to allow getting dynamic attributes on variables
+ * added Twig_Loader_Chain
+ * added Twig_Loader_Array::setTemplate()
+ * added an optimization for the set tag when used to capture a large chunk of static text
+ * changed name regex to match PHP one "[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*" (works for blocks, tags, functions, filters, and macros)
+ * removed the possibility to use the "extends" tag from a block
+ * added "if" modifier support to "for" loops
+
+* 1.1.2 (2011-07-30)
+
+ * fixed json_encode filter on PHP 5.2
+ * fixed regression introduced in 1.1.1 ({{ block(foo|lower) }})
+ * fixed inheritance when using conditional parents
+ * fixed compilation of templates when the body of a child template is not empty
+ * fixed output when a macro throws an exception
+ * fixed a parsing problem when a large chunk of text is enclosed in a comment tag
+ * added PHPDoc for all Token parsers and Core extension functions
+
+* 1.1.1 (2011-07-17)
+
+ * added a performance optimization in the Optimizer (also helps to lower the number of nested level calls)
+ * made some performance improvement for some edge cases
+
+* 1.1.0 (2011-06-28)
+
+ * fixed json_encode filter
+
+* 1.1.0-RC3 (2011-06-24)
+
+ * fixed method case-sensitivity when using the sandbox mode
+ * added timezone support for the date filter
+ * fixed possible security problems with NUL bytes
+
+* 1.1.0-RC2 (2011-06-16)
+
+ * added an exception when the template passed to "use" is not a string
+ * made 'a.b is defined' not throw an exception if a is not defined (in strict mode)
+ * added {% line \d+ %} directive
+
+* 1.1.0-RC1 (2011-05-28)
+
+Flush your cache after upgrading.
+
+ * fixed date filter when using a timestamp
+ * fixed the defined test for some cases
+ * fixed a parsing problem when a large chunk of text is enclosed in a raw tag
+ * added support for horizontal reuse of template blocks (see docs for more information)
+ * added whitespace control modifier to all tags (see docs for more information)
+ * added null as an alias for none (the null test is also an alias for the none test now)
+ * made TRUE, FALSE, NONE equivalent to their lowercase counterparts
+ * wrapped all compilation and runtime exceptions with Twig_Error_Runtime and added logic to guess the template name and line
+ * moved display() method to Twig_Template (generated templates should now use doDisplay() instead)
+
+* 1.0.0 (2011-03-27)
+
+ * fixed output when using mbstring
+ * fixed duplicate call of methods when using the sandbox
+ * made the charset configurable for the escape filter
+
+* 1.0.0-RC2 (2011-02-21)
+
+ * changed the way {% set %} works when capturing (the content is now marked as safe)
+ * added support for macro name in the endmacro tag
+ * make Twig_Error compatible with PHP 5.3.0 >
+ * fixed an infinite loop on some Windows configurations
+ * fixed the "length" filter for numbers
+ * fixed Template::getAttribute() as properties in PHP are case sensitive
+ * removed coupling between Twig_Node and Twig_Template
+ * fixed the ternary operator precedence rule
+
+* 1.0.0-RC1 (2011-01-09)
+
+Backward incompatibilities:
+
+ * the "items" filter, which has been deprecated for quite a long time now, has been removed
+ * the "range" filter has been converted to a function: 0|range(10) -> range(0, 10)
+ * the "constant" filter has been converted to a function: {{ some_date|date('DATE_W3C'|constant) }} -> {{ some_date|date(constant('DATE_W3C')) }}
+ * the "cycle" filter has been converted to a function: {{ ['odd', 'even']|cycle(i) }} -> {{ cycle(['odd', 'even'], i) }}
+ * the "for" tag does not support "joined by" anymore
+ * the "autoescape" first argument is now "true"/"false" (instead of "on"/"off")
+ * the "parent" tag has been replaced by a "parent" function ({{ parent() }} instead of {% parent %})
+ * the "display" tag has been replaced by a "block" function ({{ block('title') }} instead of {% display title %})
+ * removed the grammar and simple token parser (moved to the Twig Extensions repository)
+
+Changes:
+
+ * added "needs_context" option for filters and functions (the context is then passed as a first argument)
+ * added global variables support
+ * made macros return their value instead of echoing directly (fixes calling a macro in sandbox mode)
+ * added the "from" tag to import macros as functions
+ * added support for functions (a function is just syntactic sugar for a getAttribute() call)
+ * made macros callable when sandbox mode is enabled
+ * added an exception when a macro uses a reserved name
+ * the "default" filter now uses the "empty" test instead of just checking for null
+ * added the "empty" test
+
+* 0.9.10 (2010-12-16)
+
+Backward incompatibilities:
+
+ * The Escaper extension is enabled by default, which means that all displayed
+   variables are now automatically escaped. You can revert to the previous
+   behavior by removing the extension via $env->removeExtension('escaper')
+   or just set the 'autoescape' option to 'false'.
+ * removed the "without loop" attribute for the "for" tag (not needed anymore
+   as the Optimizer take care of that for most cases)
+ * arrays and hashes have now a different syntax
+     * arrays keep the same syntax with square brackets: [1, 2]
+     * hashes now use curly braces (["a": "b"] should now be written as {"a": "b"})
+     * support for "arrays with keys" and "hashes without keys" is not supported anymore ([1, "foo": "bar"] or {"foo": "bar", 1})
+ * the i18n extension is now part of the Twig Extensions repository
+
+Changes:
+
+ * added the merge filter
+ * removed 'is_escaper' option for filters (a left over from the previous version) -- you must use 'is_safe' now instead
+ * fixed usage of operators as method names (like is, in, and not)
+ * changed the order of execution for node visitors
+ * fixed default() filter behavior when used with strict_variables set to on
+ * fixed filesystem loader compatibility with PHAR files
+ * enhanced error messages when an unexpected token is parsed in an expression
+ * fixed filename not being added to syntax error messages
+ * added the autoescape option to enable/disable autoescaping
+ * removed the newline after a comment (mimics PHP behavior)
+ * added a syntax error exception when parent block is used on a template that does not extend another one
+ * made the Escaper extension enabled by default
+ * fixed sandbox extension when used with auto output escaping
+ * fixed escaper when wrapping a Twig_Node_Print (the original class must be preserved)
+ * added an Optimizer extension (enabled by default; optimizes "for" loops and "raw" filters)
+ * added priority to node visitors
+
+* 0.9.9 (2010-11-28)
+
+Backward incompatibilities:
+ * the self special variable has been renamed to _self
+ * the odd and even filters are now tests:
+     {{ foo|odd }} must now be written {{ foo is odd }}
+ * the "safe" filter has been renamed to "raw"
+ * in Node classes,
+        sub-nodes are now accessed via getNode() (instead of property access)
+        attributes via getAttribute() (instead of array access)
+ * the urlencode filter had been renamed to url_encode
+ * the include tag now merges the passed variables with the current context by default
+   (the old behavior is still possible by adding the "only" keyword)
+ * moved Exceptions to Twig_Error_* (Twig_SyntaxError/Twig_RuntimeError are now Twig_Error_Syntax/Twig_Error_Runtime)
+ * removed support for {{ 1 < i < 3 }} (use {{ i > 1 and i < 3 }} instead)
+ * the "in" filter has been removed ({{ a|in(b) }} should now be written {{ a in b }})
+
+Changes:
+ * added file and line to Twig_Error_Runtime exceptions thrown from Twig_Template
+ * changed trans tag to accept any variable for the plural count
+ * fixed sandbox mode (__toString() method check was not enforced if called implicitly from complex statements)
+ * added the ** (power) operator
+ * changed the algorithm used for parsing expressions
+ * added the spaceless tag
+ * removed trim_blocks option
+ * added support for is*() methods for attributes (foo.bar now looks for foo->getBar() or foo->isBar())
+ * changed all exceptions to extend Twig_Error
+ * fixed unary expressions ({{ not(1 or 0) }})
+ * fixed child templates (with an extend tag) that uses one or more imports
+ * added support for {{ 1 not in [2, 3] }} (more readable than the current {{ not (1 in [2, 3]) }})
+ * escaping has been rewritten
+ * the implementation of template inheritance has been rewritten
+   (blocks can now be called individually and still work with inheritance)
+ * fixed error handling for if tag when a syntax error occurs within a subparse process
+ * added a way to implement custom logic for resolving token parsers given a tag name
+ * fixed js escaper to be stricter (now uses a whilelist-based js escaper)
+ * added the following filers: "constant", "trans", "replace", "json_encode"
+ * added a "constant" test
+ * fixed objects with __toString() not being autoescaped
+ * fixed subscript expressions when calling __call() (methods now keep the case)
+ * added "test" feature (accessible via the "is" operator)
+ * removed the debug tag (should be done in an extension)
+ * fixed trans tag when no vars are used in plural form
+ * fixed race condition when writing template cache
+ * added the special _charset variable to reference the current charset
+ * added the special _context variable to reference the current context
+ * renamed self to _self (to avoid conflict)
+ * fixed Twig_Template::getAttribute() for protected properties
+
+* 0.9.8 (2010-06-28)
+
+Backward incompatibilities:
+ * the trans tag plural count is now attached to the plural tag:
+    old: `{% trans count %}...{% plural %}...{% endtrans %}`
+    new: `{% trans %}...{% plural count %}...{% endtrans %}`
+
+ * added a way to translate strings coming from a variable ({% trans var %})
+ * fixed trans tag when used with the Escaper extension
+ * fixed default cache umask
+ * removed Twig_Template instances from the debug tag output
+ * fixed objects with __isset() defined
+ * fixed set tag when used with a capture
+ * fixed type hinting for Twig_Environment::addFilter() method
+
+* 0.9.7 (2010-06-12)
+
+Backward incompatibilities:
+ * changed 'as' to '=' for the set tag ({% set title as "Title" %} must now be {% set title = "Title" %})
+ * removed the sandboxed attribute of the include tag (use the new sandbox tag instead)
+ * refactored the Node system (if you have custom nodes, you will have to update them to use the new API)
+
+ * added self as a special variable that refers to the current template (useful for importing macros from the current template)
+ * added Twig_Template instance support to the include tag
+ * added support for dynamic and conditional inheritance ({% extends some_var %} and {% extends standalone ? "minimum" : "base" %})
+ * added a grammar sub-framework to ease the creation of custom tags
+ * fixed the for tag for large arrays (some loop variables are now only available for arrays and objects that implement the Countable interface)
+ * removed the Twig_Resource::resolveMissingFilter() method
+ * fixed the filter tag which did not apply filtering to included files
+ * added a bunch of unit tests
+ * added a bunch of phpdoc
+ * added a sandbox tag in the sandbox extension
+ * changed the date filter to support any date format supported by DateTime
+ * added strict_variable setting to throw an exception when an invalid variable is used in a template (disabled by default)
+ * added the lexer, parser, and compiler as arguments to the Twig_Environment constructor
+ * changed the cache option to only accepts an explicit path to a cache directory or false
+ * added a way to add token parsers, filters, and visitors without creating an extension
+ * added three interfaces: Twig_NodeInterface, Twig_TokenParserInterface, and Twig_FilterInterface
+ * changed the generated code to match the new coding standards
+ * fixed sandbox mode (__toString() method check was not enforced if called implicitly from a simple statement like {{ article }})
+ * added an exception when a child template has a non-empty body (as it is always ignored when rendering)
+
+* 0.9.6 (2010-05-12)
+
+ * fixed variables defined outside a loop and for which the value changes in a for loop
+ * fixed the test suite for PHP 5.2 and older versions of PHPUnit
+ * added support for __call() in expression resolution
+ * fixed node visiting for macros (macros are now visited by visitors as any other node)
+ * fixed nested block definitions with a parent call (rarely useful but nonetheless supported now)
+ * added the cycle filter
+ * fixed the Lexer when mbstring.func_overload is used with an mbstring.internal_encoding different from ASCII
+ * added a long-syntax for the set tag ({% set foo %}...{% endset %})
+ * unit tests are now powered by PHPUnit
+ * added support for gettext via the `i18n` extension
+ * fixed twig_capitalize_string_filter() and fixed twig_length_filter() when used with UTF-8 values
+ * added a more useful exception if an if tag is not closed properly
+ * added support for escaping strategy in the autoescape tag
+ * fixed lexer when a template has a big chunk of text between/in a block
+
+* 0.9.5 (2010-01-20)
+
+As for any new release, don't forget to remove all cached templates after
+upgrading.
+
+If you have defined custom filters, you MUST upgrade them for this release. To
+upgrade, replace "array" with "new Twig_Filter_Function", and replace the
+environment constant by the "needs_environment" option:
+
+  // before
+  'even'   => array('twig_is_even_filter', false),
+  'escape' => array('twig_escape_filter', true),
+
+  // after
+  'even'   => new Twig_Filter_Function('twig_is_even_filter'),
+  'escape' => new Twig_Filter_Function('twig_escape_filter', array('needs_environment' => true)),
+
+If you have created NodeTransformer classes, you will need to upgrade them to
+the new interface (please note that the interface is not yet considered
+stable).
+
+ * fixed list nodes that did not extend the Twig_NodeListInterface
+ * added the "without loop" option to the for tag (it disables the generation of the loop variable)
+ * refactored node transformers to node visitors
+ * fixed automatic-escaping for blocks
+ * added a way to specify variables to pass to an included template
+ * changed the automatic-escaping rules to be more sensible and more configurable in custom filters (the documentation lists all the rules)
+ * improved the filter system to allow object methods to be used as filters
+ * changed the Array and String loaders to actually make use of the cache mechanism
+ * included the default filter function definitions in the extension class files directly (Core, Escaper)
+ * added the // operator (like the floor() PHP function)
+ * added the .. operator (as a syntactic sugar for the range filter when the step is 1)
+ * added the in operator (as a syntactic sugar for the in filter)
+ * added the following filters in the Core extension: in, range
+ * added support for arrays (same behavior as in PHP, a mix between lists and dictionaries, arrays and hashes)
+ * enhanced some error messages to provide better feedback in case of parsing errors
+
+* 0.9.4 (2009-12-02)
+
+If you have custom loaders, you MUST upgrade them for this release: The
+Twig_Loader base class has been removed, and the Twig_LoaderInterface has also
+been changed (see the source code for more information or the documentation).
+
+ * added support for DateTime instances for the date filter
+ * fixed loop.last when the array only has one item
+ * made it possible to insert newlines in tag and variable blocks
+ * fixed a bug when a literal '\n' were present in a template text
+ * fixed bug when the filename of a template contains */
+ * refactored loaders
+
+* 0.9.3 (2009-11-11)
+
+This release is NOT backward compatible with the previous releases.
+
+  The loaders do not take the cache and autoReload arguments anymore. Instead,
+  the Twig_Environment class has two new options: cache and auto_reload.
+  Upgrading your code means changing this kind of code:
+
+      $loader = new Twig_Loader_Filesystem('/path/to/templates', '/path/to/compilation_cache', true);
+      $twig = new Twig_Environment($loader);
+
+  to something like this:
+
+      $loader = new Twig_Loader_Filesystem('/path/to/templates');
+      $twig = new Twig_Environment($loader, array(
+        'cache' => '/path/to/compilation_cache',
+        'auto_reload' => true,
+      ));
+
+ * deprecated the "items" filter as it is not needed anymore
+ * made cache and auto_reload options of Twig_Environment instead of arguments of Twig_Loader
+ * optimized template loading speed
+ * removed output when an error occurs in a template and render() is used
+ * made major speed improvements for loops (up to 300% on even the smallest loops)
+ * added properties as part of the sandbox mode
+ * added public properties support (obj.item can now be the item property on the obj object)
+ * extended set tag to support expression as value ({% set foo as 'foo' ~ 'bar' %} )
+ * fixed bug when \ was used in HTML
+
+* 0.9.2 (2009-10-29)
+
+ * made some speed optimizations
+ * changed the cache extension to .php
+ * added a js escaping strategy
+ * added support for short block tag
+ * changed the filter tag to allow chained filters
+ * made lexer more flexible as you can now change the default delimiters
+ * added set tag
+ * changed default directory permission when cache dir does not exist (more secure)
+ * added macro support
+ * changed filters first optional argument to be a Twig_Environment instance instead of a Twig_Template instance
+ * made Twig_Autoloader::autoload() a static method
+ * avoid writing template file if an error occurs
+ * added $ escaping when outputting raw strings
+ * enhanced some error messages to ease debugging
+ * fixed empty cache files when the template contains an error
+
+* 0.9.1 (2009-10-14)
+
+  * fixed a bug in PHP 5.2.6
+  * fixed numbers with one than one decimal
+  * added support for method calls with arguments ({{ foo.bar('a', 43) }})
+  * made small speed optimizations
+  * made minor tweaks to allow better extensibility and flexibility
+
+* 0.9.0 (2009-10-12)
+
+ * Initial release
diff --git a/vendor/twig/twig/LICENSE b/vendor/twig/twig/LICENSE
new file mode 100644 (file)
index 0000000..3384cc5
--- /dev/null
@@ -0,0 +1,31 @@
+Copyright (c) 2009-2013 by the Twig Team, see AUTHORS for more details.
+
+Some rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * The names of the contributors may not be used to endorse or
+      promote products derived from this software without specific
+      prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/twig/twig/README.markdown b/vendor/twig/twig/README.markdown
new file mode 100644 (file)
index 0000000..88d6fab
--- /dev/null
@@ -0,0 +1,17 @@
+Twig, the flexible, fast, and secure template language for PHP
+==============================================================
+
+[![Build Status](https://secure.travis-ci.org/fabpot/Twig.png?branch=master)](http://travis-ci.org/fabpot/Twig)
+
+Twig is a template language for PHP, released under the new BSD license (code
+and documentation).
+
+Twig uses a syntax similar to the Django and Jinja template languages which
+inspired the Twig runtime environment.
+
+More Information
+----------------
+
+Read the [documentation][1] for more information.
+
+[1]: http://twig.sensiolabs.org/documentation
diff --git a/vendor/twig/twig/composer.json b/vendor/twig/twig/composer.json
new file mode 100644 (file)
index 0000000..67a08aa
--- /dev/null
@@ -0,0 +1,31 @@
+{
+    "name": "twig/twig",
+    "type": "library",
+    "description": "Twig, the flexible, fast, and secure template language for PHP",
+    "keywords": ["templating"],
+    "homepage": "http://twig.sensiolabs.org",
+    "license": "BSD-3-Clause",
+    "authors": [
+        {
+            "name": "Fabien Potencier",
+            "email": "fabien@symfony.com"
+        },
+        {
+            "name": "Armin Ronacher",
+            "email": "armin.ronacher@active-4.com"
+        }
+    ],
+    "require": {
+        "php": ">=5.2.4"
+    },
+    "autoload": {
+        "psr-0" : {
+            "Twig_" : "lib/"
+        }
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.13-dev"
+        }
+    }
+}
diff --git a/vendor/twig/twig/doc/advanced.rst b/vendor/twig/twig/doc/advanced.rst
new file mode 100644 (file)
index 0000000..e1945eb
--- /dev/null
@@ -0,0 +1,829 @@
+Extending Twig
+==============
+
+.. caution::
+
+    This section describes how to extend Twig as of **Twig 1.12**. If you are
+    using an older version, read the :doc:`legacy<advanced_legacy>` chapter
+    instead.
+
+Twig can be extended in many ways; you can add extra tags, filters, tests,
+operators, global variables, and functions. You can even extend the parser
+itself with node visitors.
+
+.. note::
+
+    The first section of this chapter describes how to extend Twig easily. If
+    you want to reuse your changes in different projects or if you want to
+    share them with others, you should then create an extension as described
+    in the following section.
+
+.. caution::
+
+    When extending Twig without creating an extension, Twig won't be able to
+    recompile your templates when the PHP code is updated. To see your changes
+    in real-time, either disable template caching or package your code into an
+    extension (see the next section of this chapter).
+
+Before extending Twig, you must understand the differences between all the
+different possible extension points and when to use them.
+
+First, remember that Twig has two main language constructs:
+
+* ``{{ }}``: used to print the result of an expression evaluation;
+
+* ``{% %}``: used to execute statements.
+
+To understand why Twig exposes so many extension points, let's see how to
+implement a *Lorem ipsum* generator (it needs to know the number of words to
+generate).
+
+You can use a ``lipsum`` *tag*:
+
+.. code-block:: jinja
+
+    {% lipsum 40 %}
+
+That works, but using a tag for ``lipsum`` is not a good idea for at least
+three main reasons:
+
+* ``lipsum`` is not a language construct;
+* The tag outputs something;
+* The tag is not flexible as you cannot use it in an expression:
+
+  .. code-block:: jinja
+
+      {{ 'some text' ~ {% lipsum 40 %} ~ 'some more text' }}
+
+In fact, you rarely need to create tags; and that's good news because tags are
+the most complex extension point of Twig.
+
+Now, let's use a ``lipsum`` *filter*:
+
+.. code-block:: jinja
+
+    {{ 40|lipsum }}
+
+Again, it works, but it looks weird. A filter transforms the passed value to
+something else but here we use the value to indicate the number of words to
+generate (so, ``40`` is an argument of the filter, not the value we want to
+transform).
+
+Next, let's use a ``lipsum`` *function*:
+
+.. code-block:: jinja
+
+    {{ lipsum(40) }}
+
+Here we go. For this specific example, the creation of a function is the
+extension point to use. And you can use it anywhere an expression is accepted:
+
+.. code-block:: jinja
+
+    {{ 'some text' ~ lipsum(40) ~ 'some more text' }}
+
+    {% set lipsum = lipsum(40) %}
+
+Last but not the least, you can also use a *global* object with a method able
+to generate lorem ipsum text:
+
+.. code-block:: jinja
+
+    {{ text.lipsum(40) }}
+
+As a rule of thumb, use functions for frequently used features and global
+objects for everything else.
+
+Keep in mind the following when you want to extend Twig:
+
+========== ========================== ========== =========================
+What?      Implementation difficulty? How often? When?
+========== ========================== ========== =========================
+*macro*    trivial                    frequent   Content generation
+*global*   trivial                    frequent   Helper object
+*function* trivial                    frequent   Content generation
+*filter*   trivial                    frequent   Value transformation
+*tag*      complex                    rare       DSL language construct
+*test*     trivial                    rare       Boolean decision
+*operator* trivial                    rare       Values transformation
+========== ========================== ========== =========================
+
+Globals
+-------
+
+A global variable is like any other template variable, except that it's
+available in all templates and macros::
+
+    $twig = new Twig_Environment($loader);
+    $twig->addGlobal('text', new Text());
+
+You can then use the ``text`` variable anywhere in a template:
+
+.. code-block:: jinja
+
+    {{ text.lipsum(40) }}
+
+Filters
+-------
+
+Creating a filter is as simple as associating a name with a PHP callable::
+
+    // an anonymous function
+    $filter = new Twig_SimpleFilter('rot13', function ($string) {
+        return str_rot13($string);
+    });
+
+    // or a simple PHP function
+    $filter = new Twig_SimpleFilter('rot13', 'str_rot13');
+
+    // or a class method
+    $filter = new Twig_SimpleFilter('rot13', array('SomeClass', 'rot13Filter'));
+
+The first argument passed to the ``Twig_SimpleFilter`` constructor is the name
+of the filter you will use in templates and the second one is the PHP callable
+to associate with it.
+
+Then, add the filter to your Twig environment::
+
+    $twig = new Twig_Environment($loader);
+    $twig->addFilter($filter);
+
+And here is how to use it in a template:
+
+.. code-block:: jinja
+
+    {{ 'Twig'|rot13 }}
+
+    {# will output Gjvt #}
+
+When called by Twig, the PHP callable receives the left side of the filter
+(before the pipe ``|``) as the first argument and the extra arguments passed
+to the filter (within parentheses ``()``) as extra arguments.
+
+For instance, the following code:
+
+.. code-block:: jinja
+
+    {{ 'TWIG'|lower }}
+    {{ now|date('d/m/Y') }}
+
+is compiled to something like the following::
+
+    <?php echo strtolower('TWIG') ?>
+    <?php echo twig_date_format_filter($now, 'd/m/Y') ?>
+
+The ``Twig_SimpleFilter`` class takes an array of options as its last
+argument::
+
+    $filter = new Twig_SimpleFilter('rot13', 'str_rot13', $options);
+
+Environment aware Filters
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you want to access the current environment instance in your filter, set the
+``needs_environment`` option to ``true``; Twig will pass the current
+environment as the first argument to the filter call::
+
+    $filter = new Twig_SimpleFilter('rot13', function (Twig_Environment $env, $string) {
+        // get the current charset for instance
+        $charset = $env->getCharset();
+
+        return str_rot13($string);
+    }, array('needs_environment' => true));
+
+Context aware Filters
+~~~~~~~~~~~~~~~~~~~~~
+
+If you want to access the current context in your filter, set the
+``needs_context`` option to ``true``; Twig will pass the current context as
+the first argument to the filter call (or the second one if
+``needs_environment`` is also set to ``true``)::
+
+    $filter = new Twig_SimpleFilter('rot13', function ($context, $string) {
+        // ...
+    }, array('needs_context' => true));
+
+    $filter = new Twig_SimpleFilter('rot13', function (Twig_Environment $env, $context, $string) {
+        // ...
+    }, array('needs_context' => true, 'needs_environment' => true));
+
+Automatic Escaping
+~~~~~~~~~~~~~~~~~~
+
+If automatic escaping is enabled, the output of the filter may be escaped
+before printing. If your filter acts as an escaper (or explicitly outputs html
+or JavaScript code), you will want the raw output to be printed. In such a
+case, set the ``is_safe`` option::
+
+    $filter = new Twig_SimpleFilter('nl2br', 'nl2br', array('is_safe' => array('html')));
+
+Some filters may need to work on input that is already escaped or safe, for
+example when adding (safe) html tags to originally unsafe output. In such a
+case, set the ``pre_escape`` option to escape the input data before it is run
+through your filter::
+
+    $filter = new Twig_SimpleFilter('somefilter', 'somefilter', array('pre_escape' => 'html', 'is_safe' => array('html')));
+
+Dynamic Filters
+~~~~~~~~~~~~~~~
+
+A filter name containing the special ``*`` character is a dynamic filter as
+the ``*`` can be any string::
+
+    $filter = new Twig_SimpleFilter('*_path', function ($name, $arguments) {
+        // ...
+    });
+
+The following filters will be matched by the above defined dynamic filter:
+
+* ``product_path``
+* ``category_path``
+
+A dynamic filter can define more than one dynamic parts::
+
+    $filter = new Twig_SimpleFilter('*_path_*', function ($name, $suffix, $arguments) {
+        // ...
+    });
+
+The filter will receive all dynamic part values before the normal filter
+arguments, but after the environment and the context. For instance, a call to
+``'foo'|a_path_b()`` will result in the following arguments to be passed to
+the filter: ``('a', 'b', 'foo')``.
+
+Functions
+---------
+
+Functions are defined in the exact same way as filters, but you need to create
+an instance of ``Twig_SimpleFunction``::
+
+    $twig = new Twig_Environment($loader);
+    $function = new Twig_SimpleFunction('function_name', function () {
+        // ...
+    });
+    $twig->addFunction($function);
+
+Functions support the same features as filters, except for the ``pre_escape``
+and ``preserves_safety`` options.
+
+Tests
+-----
+
+Tests are defined in the exact same way as filters and functions, but you need
+to create an instance of ``Twig_SimpleTest``::
+
+    $twig = new Twig_Environment($loader);
+    $test = new Twig_SimpleTest('test_name', function () {
+        // ...
+    });
+    $twig->addTest($test);
+
+Tests allow you to create custom application specific logic for evaluating
+boolean conditions. As a simple, example let's create a Twig test that checks if
+objects are 'red'::
+
+    $twig = new Twig_Environment($loader)
+    $test = new Twig_SimpleTest('red', function ($value) {
+        if (isset($value->color) && $value->color == 'red') {
+            return true;
+        }
+        if (isset($value->paint) && $value->paint == 'red') {
+            return true;
+        }
+        return false;
+    });
+    $twig->addTest($test);
+
+Test functions should always return true/false.
+
+When creating tests you can use the ``node_class`` option to provide custom test
+compilation. This is useful if your test can be compiled into PHP primitives.
+This is used by many of the tests built into Twig::
+
+    $twig = new Twig_Environment($loader)
+    $test = new Twig_SimpleTest(
+        'odd',
+        null,
+        array('node_class' => 'Twig_Node_Expression_Test_Odd'));
+    $twig->addTest($test);
+
+    class Twig_Node_Expression_Test_Odd extends Twig_Node_Expression_Test
+    {
+        public function compile(Twig_Compiler $compiler)
+        {
+            $compiler
+                ->raw('(')
+                ->subcompile($this->getNode('node'))
+                ->raw(' % 2 == 1')
+                ->raw(')')
+            ;
+        }
+    }
+
+The above example, shows how you can create tests that use a node class. The
+node class has access to one sub-node called 'node'. This sub-node contains the
+value that is being tested. When the ``odd`` filter is used in code like:
+
+.. code-block:: jinja
+
+    {% if my_value is odd %}
+
+The ``node`` sub-node will contain an expression of ``my_value``. Node based
+tests also have access to the ``arguments`` node. This node will contain the
+various other arguments that have been provided to your test.
+
+Tags
+----
+
+One of the most exciting feature of a template engine like Twig is the
+possibility to define new language constructs. This is also the most complex
+feature as you need to understand how Twig's internals work.
+
+Let's create a simple ``set`` tag that allows the definition of simple
+variables from within a template. The tag can be used like follows:
+
+.. code-block:: jinja
+
+    {% set name = "value" %}
+
+    {{ name }}
+
+    {# should output value #}
+
+.. note::
+
+    The ``set`` tag is part of the Core extension and as such is always
+    available. The built-in version is slightly more powerful and supports
+    multiple assignments by default (cf. the template designers chapter for
+    more information).
+
+Three steps are needed to define a new tag:
+
+* Defining a Token Parser class (responsible for parsing the template code);
+
+* Defining a Node class (responsible for converting the parsed code to PHP);
+
+* Registering the tag.
+
+Registering a new tag
+~~~~~~~~~~~~~~~~~~~~~
+
+Adding a tag is as simple as calling the ``addTokenParser`` method on the
+``Twig_Environment`` instance::
+
+    $twig = new Twig_Environment($loader);
+    $twig->addTokenParser(new Project_Set_TokenParser());
+
+Defining a Token Parser
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Now, let's see the actual code of this class::
+
+    class Project_Set_TokenParser extends Twig_TokenParser
+    {
+        public function parse(Twig_Token $token)
+        {
+            $parser = $this->parser;
+            $stream = $parser->getStream();
+
+            $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
+            $stream->expect(Twig_Token::OPERATOR_TYPE, '=');
+            $value = $parser->getExpressionParser()->parseExpression();
+            $stream->expect(Twig_Token::BLOCK_END_TYPE);
+
+            return new Project_Set_Node($name, $value, $token->getLine(), $this->getTag());
+        }
+
+        public function getTag()
+        {
+            return 'set';
+        }
+    }
+
+The ``getTag()`` method must return the tag we want to parse, here ``set``.
+
+The ``parse()`` method is invoked whenever the parser encounters a ``set``
+tag. It should return a ``Twig_Node`` instance that represents the node (the
+``Project_Set_Node`` calls creating is explained in the next section).
+
+The parsing process is simplified thanks to a bunch of methods you can call
+from the token stream (``$this->parser->getStream()``):
+
+* ``getCurrent()``: Gets the current token in the stream.
+
+* ``next()``: Moves to the next token in the stream, *but returns the old one*.
+
+* ``test($type)``, ``test($value)`` or ``test($type, $value)``: Determines whether
+  the current token is of a particular type or value (or both). The value may be an
+  array of several possible values.
+
+* ``expect($type[, $value[, $message]])``: If the current token isn't of the given
+  type/value a syntax error is thrown. Otherwise, if the type and value are correct,
+  the token is returned and the stream moves to the next token.
+
+* ``look()``: Looks a the next token without consuming it.
+
+Parsing expressions is done by calling the ``parseExpression()`` like we did for
+the ``set`` tag.
+
+.. tip::
+
+    Reading the existing ``TokenParser`` classes is the best way to learn all
+    the nitty-gritty details of the parsing process.
+
+Defining a Node
+~~~~~~~~~~~~~~~
+
+The ``Project_Set_Node`` class itself is rather simple::
+
+    class Project_Set_Node extends Twig_Node
+    {
+        public function __construct($name, Twig_Node_Expression $value, $line, $tag = null)
+        {
+            parent::__construct(array('value' => $value), array('name' => $name), $line, $tag);
+        }
+
+        public function compile(Twig_Compiler $compiler)
+        {
+            $compiler
+                ->addDebugInfo($this)
+                ->write('$context[\''.$this->getAttribute('name').'\'] = ')
+                ->subcompile($this->getNode('value'))
+                ->raw(";\n")
+            ;
+        }
+    }
+
+The compiler implements a fluid interface and provides methods that helps the
+developer generate beautiful and readable PHP code:
+
+* ``subcompile()``: Compiles a node.
+
+* ``raw()``: Writes the given string as is.
+
+* ``write()``: Writes the given string by adding indentation at the beginning
+  of each line.
+
+* ``string()``: Writes a quoted string.
+
+* ``repr()``: Writes a PHP representation of a given value (see
+  ``Twig_Node_For`` for a usage example).
+
+* ``addDebugInfo()``: Adds the line of the original template file related to
+  the current node as a comment.
+
+* ``indent()``: Indents the generated code (see ``Twig_Node_Block`` for a
+  usage example).
+
+* ``outdent()``: Outdents the generated code (see ``Twig_Node_Block`` for a
+  usage example).
+
+.. _creating_extensions:
+
+Creating an Extension
+---------------------
+
+The main motivation for writing an extension is to move often used code into a
+reusable class like adding support for internationalization. An extension can
+define tags, filters, tests, operators, global variables, functions, and node
+visitors.
+
+Creating an extension also makes for a better separation of code that is
+executed at compilation time and code needed at runtime. As such, it makes
+your code faster.
+
+Most of the time, it is useful to create a single extension for your project,
+to host all the specific tags and filters you want to add to Twig.
+
+.. tip::
+
+    When packaging your code into an extension, Twig is smart enough to
+    recompile your templates whenever you make a change to it (when
+    ``auto_reload`` is enabled).
+
+.. note::
+
+    Before writing your own extensions, have a look at the Twig official
+    extension repository: http://github.com/fabpot/Twig-extensions.
+
+An extension is a class that implements the following interface::
+
+    interface Twig_ExtensionInterface
+    {
+        /**
+         * Initializes the runtime environment.
+         *
+         * This is where you can load some file that contains filter functions for instance.
+         *
+         * @param Twig_Environment $environment The current Twig_Environment instance
+         */
+        function initRuntime(Twig_Environment $environment);
+
+        /**
+         * Returns the token parser instances to add to the existing list.
+         *
+         * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
+         */
+        function getTokenParsers();
+
+        /**
+         * Returns the node visitor instances to add to the existing list.
+         *
+         * @return array An array of Twig_NodeVisitorInterface instances
+         */
+        function getNodeVisitors();
+
+        /**
+         * Returns a list of filters to add to the existing list.
+         *
+         * @return array An array of filters
+         */
+        function getFilters();
+
+        /**
+         * Returns a list of tests to add to the existing list.
+         *
+         * @return array An array of tests
+         */
+        function getTests();
+
+        /**
+         * Returns a list of functions to add to the existing list.
+         *
+         * @return array An array of functions
+         */
+        function getFunctions();
+
+        /**
+         * Returns a list of operators to add to the existing list.
+         *
+         * @return array An array of operators
+         */
+        function getOperators();
+
+        /**
+         * Returns a list of global variables to add to the existing list.
+         *
+         * @return array An array of global variables
+         */
+        function getGlobals();
+
+        /**
+         * Returns the name of the extension.
+         *
+         * @return string The extension name
+         */
+        function getName();
+    }
+
+To keep your extension class clean and lean, it can inherit from the built-in
+``Twig_Extension`` class instead of implementing the whole interface. That
+way, you just need to implement the ``getName()`` method as the
+``Twig_Extension`` provides empty implementations for all other methods.
+
+The ``getName()`` method must return a unique identifier for your extension.
+
+Now, with this information in mind, let's create the most basic extension
+possible::
+
+    class Project_Twig_Extension extends Twig_Extension
+    {
+        public function getName()
+        {
+            return 'project';
+        }
+    }
+
+.. note::
+
+    Of course, this extension does nothing for now. We will customize it in
+    the next sections.
+
+Twig does not care where you save your extension on the filesystem, as all
+extensions must be registered explicitly to be available in your templates.
+
+You can register an extension by using the ``addExtension()`` method on your
+main ``Environment`` object::
+
+    $twig = new Twig_Environment($loader);
+    $twig->addExtension(new Project_Twig_Extension());
+
+Of course, you need to first load the extension file by either using
+``require_once()`` or by using an autoloader (see `spl_autoload_register()`_).
+
+.. tip::
+
+    The bundled extensions are great examples of how extensions work.
+
+Globals
+~~~~~~~
+
+Global variables can be registered in an extension via the ``getGlobals()``
+method::
+
+    class Project_Twig_Extension extends Twig_Extension
+    {
+        public function getGlobals()
+        {
+            return array(
+                'text' => new Text(),
+            );
+        }
+
+        // ...
+    }
+
+Functions
+~~~~~~~~~
+
+Functions can be registered in an extension via the ``getFunctions()``
+method::
+
+    class Project_Twig_Extension extends Twig_Extension
+    {
+        public function getFunctions()
+        {
+            return array(
+                new Twig_SimpleFunction('lipsum', 'generate_lipsum'),
+            );
+        }
+
+        // ...
+    }
+
+Filters
+~~~~~~~
+
+To add a filter to an extension, you need to override the ``getFilters()``
+method. This method must return an array of filters to add to the Twig
+environment::
+
+    class Project_Twig_Extension extends Twig_Extension
+    {
+        public function getFilters()
+        {
+            return array(
+                new Twig_SimpleFilter('rot13', 'str_rot13'),
+            );
+        }
+
+        // ...
+    }
+
+Tags
+~~~~
+
+Adding a tag in an extension can be done by overriding the
+``getTokenParsers()`` method. This method must return an array of tags to add
+to the Twig environment::
+
+    class Project_Twig_Extension extends Twig_Extension
+    {
+        public function getTokenParsers()
+        {
+            return array(new Project_Set_TokenParser());
+        }
+
+        // ...
+    }
+
+In the above code, we have added a single new tag, defined by the
+``Project_Set_TokenParser`` class. The ``Project_Set_TokenParser`` class is
+responsible for parsing the tag and compiling it to PHP.
+
+Operators
+~~~~~~~~~
+
+The ``getOperators()`` methods allows to add new operators. Here is how to add
+``!``, ``||``, and ``&&`` operators::
+
+    class Project_Twig_Extension extends Twig_Extension
+    {
+        public function getOperators()
+        {
+            return array(
+                array(
+                    '!' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'),
+                ),
+                array(
+                    '||' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
+                    '&&' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
+                ),
+            );
+        }
+
+        // ...
+    }
+
+Tests
+~~~~~
+
+The ``getTests()`` methods allows to add new test functions::
+
+    class Project_Twig_Extension extends Twig_Extension
+    {
+        public function getTests()
+        {
+            return array(
+                new Twig_SimpleTest('even', 'twig_test_even'),
+            );
+        }
+
+        // ...
+    }
+
+Overloading
+-----------
+
+To overload an already defined filter, test, operator, global variable, or
+function, define it again **as late as possible**::
+
+    $twig = new Twig_Environment($loader);
+    $twig->addFilter(new Twig_SimpleFilter('date', function ($timestamp, $format = 'F j, Y H:i') {
+        // do something different from the built-in date filter
+    }));
+
+Here, we have overloaded the built-in ``date`` filter with a custom one.
+
+That also works with an extension::
+
+    class MyCoreExtension extends Twig_Extension
+    {
+        public function getFilters()
+        {
+            return array(
+                new Twig_SimpleFilter('date', array($this, 'dateFilter')),
+            );
+        }
+
+        public function dateFilter($timestamp, $format = 'F j, Y H:i')
+        {
+            // do something different from the built-in date filter
+        }
+
+        public function getName()
+        {
+            return 'project';
+        }
+    }
+
+    $twig = new Twig_Environment($loader);
+    $twig->addExtension(new MyCoreExtension());
+
+.. caution::
+
+    Note that overloading the built-in Twig elements is not recommended as it
+    might be confusing.
+
+Testing an Extension
+--------------------
+
+Functional Tests
+~~~~~~~~~~~~~~~~
+
+You can create functional tests for extensions simply by creating the
+following file structure in your test directory::
+
+    Fixtures/
+        filters/
+            foo.test
+            bar.test
+        functions/
+            foo.test
+            bar.test
+        tags/
+            foo.test
+            bar.test
+    IntegrationTest.php
+
+The ``IntegrationTest.php`` file should look like this::
+
+    class Project_Tests_IntegrationTest extends Twig_Test_IntegrationTestCase
+    {
+        public function getExtensions()
+        {
+            return array(
+                new Project_Twig_Extension1(),
+                new Project_Twig_Extension2(),
+            );
+        }
+
+        public function getFixturesDir()
+        {
+            return dirname(__FILE__).'/Fixtures/';
+        }
+    }
+
+Fixtures examples can be found within the Twig repository
+`tests/Twig/Fixtures`_ directory.
+
+Node Tests
+~~~~~~~~~~
+
+Testing the node visitors can be complex, so extend your test cases from
+``Twig_Test_NodeTestCase``. Examples can be found in the Twig repository
+`tests/Twig/Node`_ directory.
+
+.. _`spl_autoload_register()`: http://www.php.net/spl_autoload_register
+.. _`rot13`:                   http://www.php.net/manual/en/function.str-rot13.php
+.. _`tests/Twig/Fixtures`:     https://github.com/fabpot/Twig/tree/master/test/Twig/Tests/Fixtures
+.. _`tests/Twig/Node`:         https://github.com/fabpot/Twig/tree/master/test/Twig/Tests/Node
diff --git a/vendor/twig/twig/doc/advanced_legacy.rst b/vendor/twig/twig/doc/advanced_legacy.rst
new file mode 100644 (file)
index 0000000..3d34f93
--- /dev/null
@@ -0,0 +1,887 @@
+Extending Twig
+==============
+
+.. caution::
+
+    This section describes how to extends Twig for versions **older than
+    1.12**. If you are using a newer version, read the :doc:`newer<advanced>`
+    chapter instead.
+
+Twig can be extended in many ways; you can add extra tags, filters, tests,
+operators, global variables, and functions. You can even extend the parser
+itself with node visitors.
+
+.. note::
+
+    The first section of this chapter describes how to extend Twig easily. If
+    you want to reuse your changes in different projects or if you want to
+    share them with others, you should then create an extension as described
+    in the following section.
+
+.. caution::
+
+    When extending Twig by calling methods on the Twig environment instance,
+    Twig won't be able to recompile your templates when the PHP code is
+    updated. To see your changes in real-time, either disable template caching
+    or package your code into an extension (see the next section of this
+    chapter).
+
+Before extending Twig, you must understand the differences between all the
+different possible extension points and when to use them.
+
+First, remember that Twig has two main language constructs:
+
+* ``{{ }}``: used to print the result of an expression evaluation;
+
+* ``{% %}``: used to execute statements.
+
+To understand why Twig exposes so many extension points, let's see how to
+implement a *Lorem ipsum* generator (it needs to know the number of words to
+generate).
+
+You can use a ``lipsum`` *tag*:
+
+.. code-block:: jinja
+
+    {% lipsum 40 %}
+
+That works, but using a tag for ``lipsum`` is not a good idea for at least
+three main reasons:
+
+* ``lipsum`` is not a language construct;
+* The tag outputs something;
+* The tag is not flexible as you cannot use it in an expression:
+
+  .. code-block:: jinja
+
+      {{ 'some text' ~ {% lipsum 40 %} ~ 'some more text' }}
+
+In fact, you rarely need to create tags; and that's good news because tags are
+the most complex extension point of Twig.
+
+Now, let's use a ``lipsum`` *filter*:
+
+.. code-block:: jinja
+
+    {{ 40|lipsum }}
+
+Again, it works, but it looks weird. A filter transforms the passed value to
+something else but here we use the value to indicate the number of words to
+generate (so, ``40`` is an argument of the filter, not the value we want to
+transform).
+
+Next, let's use a ``lipsum`` *function*:
+
+.. code-block:: jinja
+
+    {{ lipsum(40) }}
+
+Here we go. For this specific example, the creation of a function is the
+extension point to use. And you can use it anywhere an expression is accepted:
+
+.. code-block:: jinja
+
+    {{ 'some text' ~ ipsum(40) ~ 'some more text' }}
+
+    {% set ipsum = ipsum(40) %}
+
+Last but not the least, you can also use a *global* object with a method able
+to generate lorem ipsum text:
+
+.. code-block:: jinja
+
+    {{ text.lipsum(40) }}
+
+As a rule of thumb, use functions for frequently used features and global
+objects for everything else.
+
+Keep in mind the following when you want to extend Twig:
+
+========== ========================== ========== =========================
+What?      Implementation difficulty? How often? When?
+========== ========================== ========== =========================
+*macro*    trivial                    frequent   Content generation
+*global*   trivial                    frequent   Helper object
+*function* trivial                    frequent   Content generation
+*filter*   trivial                    frequent   Value transformation
+*tag*      complex                    rare       DSL language construct
+*test*     trivial                    rare       Boolean decision
+*operator* trivial                    rare       Values transformation
+========== ========================== ========== =========================
+
+Globals
+-------
+
+A global variable is like any other template variable, except that it's
+available in all templates and macros::
+
+    $twig = new Twig_Environment($loader);
+    $twig->addGlobal('text', new Text());
+
+You can then use the ``text`` variable anywhere in a template:
+
+.. code-block:: jinja
+
+    {{ text.lipsum(40) }}
+
+Filters
+-------
+
+A filter is a regular PHP function or an object method that takes the left
+side of the filter (before the pipe ``|``) as first argument and the extra
+arguments passed to the filter (within parentheses ``()``) as extra arguments.
+
+Defining a filter is as easy as associating the filter name with a PHP
+callable. For instance, let's say you have the following code in a template:
+
+.. code-block:: jinja
+
+    {{ 'TWIG'|lower }}
+
+When compiling this template to PHP, Twig looks for the PHP callable
+associated with the ``lower`` filter. The ``lower`` filter is a built-in Twig
+filter, and it is simply mapped to the PHP ``strtolower()`` function. After
+compilation, the generated PHP code is roughly equivalent to:
+
+.. code-block:: html+php
+
+    <?php echo strtolower('TWIG') ?>
+
+As you can see, the ``'TWIG'`` string is passed as a first argument to the PHP
+function.
+
+A filter can also take extra arguments like in the following example:
+
+.. code-block:: jinja
+
+    {{ now|date('d/m/Y') }}
+
+In this case, the extra arguments are passed to the function after the main
+argument, and the compiled code is equivalent to:
+
+.. code-block:: html+php
+
+    <?php echo twig_date_format_filter($now, 'd/m/Y') ?>
+
+Let's see how to create a new filter.
+
+In this section, we will create a ``rot13`` filter, which should return the
+`rot13`_ transformation of a string. Here is an example of its usage and the
+expected output:
+
+.. code-block:: jinja
+
+    {{ "Twig"|rot13 }}
+
+    {# should displays Gjvt #}
+
+Adding a filter is as simple as calling the ``addFilter()`` method on the
+``Twig_Environment`` instance::
+
+    $twig = new Twig_Environment($loader);
+    $twig->addFilter('rot13', new Twig_Filter_Function('str_rot13'));
+
+The second argument of ``addFilter()`` is an instance of ``Twig_Filter``.
+Here, we use ``Twig_Filter_Function`` as the filter is a PHP function. The
+first argument passed to the ``Twig_Filter_Function`` constructor is the name
+of the PHP function to call, here ``str_rot13``, a native PHP function.
+
+Let's say I now want to be able to add a prefix before the converted string:
+
+.. code-block:: jinja
+
+    {{ "Twig"|rot13('prefix_') }}
+
+    {# should displays prefix_Gjvt #}
+
+As the PHP ``str_rot13()`` function does not support this requirement, let's
+create a new PHP function::
+
+    function project_compute_rot13($string, $prefix = '')
+    {
+        return $prefix.str_rot13($string);
+    }
+
+As you can see, the ``prefix`` argument of the filter is passed as an extra
+argument to the ``project_compute_rot13()`` function.
+
+Adding this filter is as easy as before::
+
+    $twig->addFilter('rot13', new Twig_Filter_Function('project_compute_rot13'));
+
+For better encapsulation, a filter can also be defined as a static method of a
+class. The ``Twig_Filter_Function`` class can also be used to register such
+static methods as filters::
+
+    $twig->addFilter('rot13', new Twig_Filter_Function('SomeClass::rot13Filter'));
+
+.. tip::
+
+    In an extension, you can also define a filter as a static method of the
+    extension class.
+
+Environment aware Filters
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``Twig_Filter`` classes take options as their last argument. For instance,
+if you want access to the current environment instance in your filter, set the
+``needs_environment`` option to ``true``::
+
+    $filter = new Twig_Filter_Function('str_rot13', array('needs_environment' => true));
+
+Twig will then pass the current environment as the first argument to the
+filter call::
+
+    function twig_compute_rot13(Twig_Environment $env, $string)
+    {
+        // get the current charset for instance
+        $charset = $env->getCharset();
+
+        return str_rot13($string);
+    }
+
+Automatic Escaping
+~~~~~~~~~~~~~~~~~~
+
+If automatic escaping is enabled, the output of the filter may be escaped
+before printing. If your filter acts as an escaper (or explicitly outputs html
+or javascript code), you will want the raw output to be printed. In such a
+case, set the ``is_safe`` option::
+
+    $filter = new Twig_Filter_Function('nl2br', array('is_safe' => array('html')));
+
+Some filters may need to work on input that is already escaped or safe, for
+example when adding (safe) html tags to originally unsafe output. In such a
+case, set the ``pre_escape`` option to escape the input data before it is run
+through your filter::
+
+    $filter = new Twig_Filter_Function('somefilter', array('pre_escape' => 'html', 'is_safe' => array('html')));
+
+Dynamic Filters
+~~~~~~~~~~~~~~~
+
+.. versionadded:: 1.5
+    Dynamic filters support was added in Twig 1.5.
+
+A filter name containing the special ``*`` character is a dynamic filter as
+the ``*`` can be any string::
+
+    $twig->addFilter('*_path_*', new Twig_Filter_Function('twig_path'));
+
+    function twig_path($name, $arguments)
+    {
+        // ...
+    }
+
+The following filters will be matched by the above defined dynamic filter:
+
+* ``product_path``
+* ``category_path``
+
+A dynamic filter can define more than one dynamic parts::
+
+    $twig->addFilter('*_path_*', new Twig_Filter_Function('twig_path'));
+
+    function twig_path($name, $suffix, $arguments)
+    {
+        // ...
+    }
+
+The filter will receive all dynamic part values before the normal filters
+arguments. For instance, a call to ``'foo'|a_path_b()`` will result in the
+following PHP call: ``twig_path('a', 'b', 'foo')``.
+
+Functions
+---------
+
+A function is a regular PHP function or an object method that can be called from
+templates.
+
+.. code-block:: jinja
+
+    {{ constant("DATE_W3C") }}
+
+When compiling this template to PHP, Twig looks for the PHP callable
+associated with the ``constant`` function. The ``constant`` function is a built-in Twig
+function, and it is simply mapped to the PHP ``constant()`` function. After
+compilation, the generated PHP code is roughly equivalent to:
+
+.. code-block:: html+php
+
+    <?php echo constant('DATE_W3C') ?>
+
+Adding a function is similar to adding a filter. This can be done by calling the
+``addFunction()`` method on the ``Twig_Environment`` instance::
+
+    $twig = new Twig_Environment($loader);
+    $twig->addFunction('functionName', new Twig_Function_Function('someFunction'));
+
+You can also expose extension methods as functions in your templates::
+
+    // $this is an object that implements Twig_ExtensionInterface.
+    $twig = new Twig_Environment($loader);
+    $twig->addFunction('otherFunction', new Twig_Function_Method($this, 'someMethod'));
+
+Functions also support ``needs_environment`` and ``is_safe`` parameters.
+
+Dynamic Functions
+~~~~~~~~~~~~~~~~~
+
+.. versionadded:: 1.5
+    Dynamic functions support was added in Twig 1.5.
+
+A function name containing the special ``*`` character is a dynamic function
+as the ``*`` can be any string::
+
+    $twig->addFunction('*_path', new Twig_Function_Function('twig_path'));
+
+    function twig_path($name, $arguments)
+    {
+        // ...
+    }
+
+The following functions will be matched by the above defined dynamic function:
+
+* ``product_path``
+* ``category_path``
+
+A dynamic function can define more than one dynamic parts::
+
+    $twig->addFilter('*_path_*', new Twig_Filter_Function('twig_path'));
+
+    function twig_path($name, $suffix, $arguments)
+    {
+        // ...
+    }
+
+The function will receive all dynamic part values before the normal functions
+arguments. For instance, a call to ``a_path_b('foo')`` will result in the
+following PHP call: ``twig_path('a', 'b', 'foo')``.
+
+Tags
+----
+
+One of the most exciting feature of a template engine like Twig is the
+possibility to define new language constructs. This is also the most complex
+feature as you need to understand how Twig's internals work.
+
+Let's create a simple ``set`` tag that allows the definition of simple
+variables from within a template. The tag can be used like follows:
+
+.. code-block:: jinja
+
+    {% set name = "value" %}
+
+    {{ name }}
+
+    {# should output value #}
+
+.. note::
+
+    The ``set`` tag is part of the Core extension and as such is always
+    available. The built-in version is slightly more powerful and supports
+    multiple assignments by default (cf. the template designers chapter for
+    more information).
+
+Three steps are needed to define a new tag:
+
+* Defining a Token Parser class (responsible for parsing the template code);
+
+* Defining a Node class (responsible for converting the parsed code to PHP);
+
+* Registering the tag.
+
+Registering a new tag
+~~~~~~~~~~~~~~~~~~~~~
+
+Adding a tag is as simple as calling the ``addTokenParser`` method on the
+``Twig_Environment`` instance::
+
+    $twig = new Twig_Environment($loader);
+    $twig->addTokenParser(new Project_Set_TokenParser());
+
+Defining a Token Parser
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Now, let's see the actual code of this class::
+
+    class Project_Set_TokenParser extends Twig_TokenParser
+    {
+        public function parse(Twig_Token $token)
+        {
+            $lineno = $token->getLine();
+            $name = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE)->getValue();
+            $this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, '=');
+            $value = $this->parser->getExpressionParser()->parseExpression();
+
+            $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
+
+            return new Project_Set_Node($name, $value, $lineno, $this->getTag());
+        }
+
+        public function getTag()
+        {
+            return 'set';
+        }
+    }
+
+The ``getTag()`` method must return the tag we want to parse, here ``set``.
+
+The ``parse()`` method is invoked whenever the parser encounters a ``set``
+tag. It should return a ``Twig_Node`` instance that represents the node (the
+``Project_Set_Node`` calls creating is explained in the next section).
+
+The parsing process is simplified thanks to a bunch of methods you can call
+from the token stream (``$this->parser->getStream()``):
+
+* ``getCurrent()``: Gets the current token in the stream.
+
+* ``next()``: Moves to the next token in the stream, *but returns the old one*.
+
+* ``test($type)``, ``test($value)`` or ``test($type, $value)``: Determines whether
+  the current token is of a particular type or value (or both). The value may be an
+  array of several possible values.
+
+* ``expect($type[, $value[, $message]])``: If the current token isn't of the given
+  type/value a syntax error is thrown. Otherwise, if the type and value are correct,
+  the token is returned and the stream moves to the next token.
+
+* ``look()``: Looks a the next token without consuming it.
+
+Parsing expressions is done by calling the ``parseExpression()`` like we did for
+the ``set`` tag.
+
+.. tip::
+
+    Reading the existing ``TokenParser`` classes is the best way to learn all
+    the nitty-gritty details of the parsing process.
+
+Defining a Node
+~~~~~~~~~~~~~~~
+
+The ``Project_Set_Node`` class itself is rather simple::
+
+    class Project_Set_Node extends Twig_Node
+    {
+        public function __construct($name, Twig_Node_Expression $value, $lineno, $tag = null)
+        {
+            parent::__construct(array('value' => $value), array('name' => $name), $lineno, $tag);
+        }
+
+        public function compile(Twig_Compiler $compiler)
+        {
+            $compiler
+                ->addDebugInfo($this)
+                ->write('$context[\''.$this->getAttribute('name').'\'] = ')
+                ->subcompile($this->getNode('value'))
+                ->raw(";\n")
+            ;
+        }
+    }
+
+The compiler implements a fluid interface and provides methods that helps the
+developer generate beautiful and readable PHP code:
+
+* ``subcompile()``: Compiles a node.
+
+* ``raw()``: Writes the given string as is.
+
+* ``write()``: Writes the given string by adding indentation at the beginning
+  of each line.
+
+* ``string()``: Writes a quoted string.
+
+* ``repr()``: Writes a PHP representation of a given value (see
+  ``Twig_Node_For`` for a usage example).
+
+* ``addDebugInfo()``: Adds the line of the original template file related to
+  the current node as a comment.
+
+* ``indent()``: Indents the generated code (see ``Twig_Node_Block`` for a
+  usage example).
+
+* ``outdent()``: Outdents the generated code (see ``Twig_Node_Block`` for a
+  usage example).
+
+.. _creating_extensions:
+
+Creating an Extension
+---------------------
+
+The main motivation for writing an extension is to move often used code into a
+reusable class like adding support for internationalization. An extension can
+define tags, filters, tests, operators, global variables, functions, and node
+visitors.
+
+Creating an extension also makes for a better separation of code that is
+executed at compilation time and code needed at runtime. As such, it makes
+your code faster.
+
+Most of the time, it is useful to create a single extension for your project,
+to host all the specific tags and filters you want to add to Twig.
+
+.. tip::
+
+    When packaging your code into an extension, Twig is smart enough to
+    recompile your templates whenever you make a change to it (when the
+    ``auto_reload`` is enabled).
+
+.. note::
+
+    Before writing your own extensions, have a look at the Twig official
+    extension repository: http://github.com/fabpot/Twig-extensions.
+
+An extension is a class that implements the following interface::
+
+    interface Twig_ExtensionInterface
+    {
+        /**
+         * Initializes the runtime environment.
+         *
+         * This is where you can load some file that contains filter functions for instance.
+         *
+         * @param Twig_Environment $environment The current Twig_Environment instance
+         */
+        function initRuntime(Twig_Environment $environment);
+
+        /**
+         * Returns the token parser instances to add to the existing list.
+         *
+         * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
+         */
+        function getTokenParsers();
+
+        /**
+         * Returns the node visitor instances to add to the existing list.
+         *
+         * @return array An array of Twig_NodeVisitorInterface instances
+         */
+        function getNodeVisitors();
+
+        /**
+         * Returns a list of filters to add to the existing list.
+         *
+         * @return array An array of filters
+         */
+        function getFilters();
+
+        /**
+         * Returns a list of tests to add to the existing list.
+         *
+         * @return array An array of tests
+         */
+        function getTests();
+
+        /**
+         * Returns a list of functions to add to the existing list.
+         *
+         * @return array An array of functions
+         */
+        function getFunctions();
+
+        /**
+         * Returns a list of operators to add to the existing list.
+         *
+         * @return array An array of operators
+         */
+        function getOperators();
+
+        /**
+         * Returns a list of global variables to add to the existing list.
+         *
+         * @return array An array of global variables
+         */
+        function getGlobals();
+
+        /**
+         * Returns the name of the extension.
+         *
+         * @return string The extension name
+         */
+        function getName();
+    }
+
+To keep your extension class clean and lean, it can inherit from the built-in
+``Twig_Extension`` class instead of implementing the whole interface. That
+way, you just need to implement the ``getName()`` method as the
+``Twig_Extension`` provides empty implementations for all other methods.
+
+The ``getName()`` method must return a unique identifier for your extension.
+
+Now, with this information in mind, let's create the most basic extension
+possible::
+
+    class Project_Twig_Extension extends Twig_Extension
+    {
+        public function getName()
+        {
+            return 'project';
+        }
+    }
+
+.. note::
+
+    Of course, this extension does nothing for now. We will customize it in
+    the next sections.
+
+Twig does not care where you save your extension on the filesystem, as all
+extensions must be registered explicitly to be available in your templates.
+
+You can register an extension by using the ``addExtension()`` method on your
+main ``Environment`` object::
+
+    $twig = new Twig_Environment($loader);
+    $twig->addExtension(new Project_Twig_Extension());
+
+Of course, you need to first load the extension file by either using
+``require_once()`` or by using an autoloader (see `spl_autoload_register()`_).
+
+.. tip::
+
+    The bundled extensions are great examples of how extensions work.
+
+Globals
+~~~~~~~
+
+Global variables can be registered in an extension via the ``getGlobals()``
+method::
+
+    class Project_Twig_Extension extends Twig_Extension
+    {
+        public function getGlobals()
+        {
+            return array(
+                'text' => new Text(),
+            );
+        }
+
+        // ...
+    }
+
+Functions
+~~~~~~~~~
+
+Functions can be registered in an extension via the ``getFunctions()``
+method::
+
+    class Project_Twig_Extension extends Twig_Extension
+    {
+        public function getFunctions()
+        {
+            return array(
+                'lipsum' => new Twig_Function_Function('generate_lipsum'),
+            );
+        }
+
+        // ...
+    }
+
+Filters
+~~~~~~~
+
+To add a filter to an extension, you need to override the ``getFilters()``
+method. This method must return an array of filters to add to the Twig
+environment::
+
+    class Project_Twig_Extension extends Twig_Extension
+    {
+        public function getFilters()
+        {
+            return array(
+                'rot13' => new Twig_Filter_Function('str_rot13'),
+            );
+        }
+
+        // ...
+    }
+
+As you can see in the above code, the ``getFilters()`` method returns an array
+where keys are the name of the filters (``rot13``) and the values the
+definition of the filter (``new Twig_Filter_Function('str_rot13')``).
+
+As seen in the previous chapter, you can also define filters as static methods
+on the extension class::
+
+$twig->addFilter('rot13', new Twig_Filter_Function('Project_Twig_Extension::rot13Filter'));
+
+You can also use ``Twig_Filter_Method`` instead of ``Twig_Filter_Function``
+when defining a filter to use a method::
+
+    class Project_Twig_Extension extends Twig_Extension
+    {
+        public function getFilters()
+        {
+            return array(
+                'rot13' => new Twig_Filter_Method($this, 'rot13Filter'),
+            );
+        }
+
+        public function rot13Filter($string)
+        {
+            return str_rot13($string);
+        }
+
+        // ...
+    }
+
+The first argument of the ``Twig_Filter_Method`` constructor is always
+``$this``, the current extension object. The second one is the name of the
+method to call.
+
+Using methods for filters is a great way to package your filter without
+polluting the global namespace. This also gives the developer more flexibility
+at the cost of a small overhead.
+
+Overriding default Filters
+..........................
+
+If some default core filters do not suit your needs, you can easily override
+them by creating your own extension. Just use the same names as the one you
+want to override::
+
+    class MyCoreExtension extends Twig_Extension
+    {
+        public function getFilters()
+        {
+            return array(
+                'date' => new Twig_Filter_Method($this, 'dateFilter'),
+                // ...
+            );
+        }
+
+        public function dateFilter($timestamp, $format = 'F j, Y H:i')
+        {
+            return '...'.twig_date_format_filter($timestamp, $format);
+        }
+
+        public function getName()
+        {
+            return 'project';
+        }
+    }
+
+Here, we override the ``date`` filter with a custom one. Using this extension
+is as simple as registering the ``MyCoreExtension`` extension by calling the
+``addExtension()`` method on the environment instance::
+
+    $twig = new Twig_Environment($loader);
+    $twig->addExtension(new MyCoreExtension());
+
+Tags
+~~~~
+
+Adding a tag in an extension can be done by overriding the
+``getTokenParsers()`` method. This method must return an array of tags to add
+to the Twig environment::
+
+    class Project_Twig_Extension extends Twig_Extension
+    {
+        public function getTokenParsers()
+        {
+            return array(new Project_Set_TokenParser());
+        }
+
+        // ...
+    }
+
+In the above code, we have added a single new tag, defined by the
+``Project_Set_TokenParser`` class. The ``Project_Set_TokenParser`` class is
+responsible for parsing the tag and compiling it to PHP.
+
+Operators
+~~~~~~~~~
+
+The ``getOperators()`` methods allows to add new operators. Here is how to add
+``!``, ``||``, and ``&&`` operators::
+
+    class Project_Twig_Extension extends Twig_Extension
+    {
+        public function getOperators()
+        {
+            return array(
+                array(
+                    '!' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'),
+                ),
+                array(
+                    '||' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
+                    '&&' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
+                ),
+            );
+        }
+
+        // ...
+    }
+
+Tests
+~~~~~
+
+The ``getTests()`` methods allows to add new test functions::
+
+    class Project_Twig_Extension extends Twig_Extension
+    {
+        public function getTests()
+        {
+            return array(
+                'even' => new Twig_Test_Function('twig_test_even'),
+            );
+        }
+
+        // ...
+    }
+
+Testing an Extension
+--------------------
+
+.. versionadded:: 1.10
+    Support for functional tests was added in Twig 1.10.
+
+Functional Tests
+~~~~~~~~~~~~~~~~
+
+You can create functional tests for extensions simply by creating the
+following file structure in your test directory::
+
+    Fixtures/
+        filters/
+            foo.test
+            bar.test
+        functions/
+            foo.test
+            bar.test
+        tags/
+            foo.test
+            bar.test
+    IntegrationTest.php
+
+The ``IntegrationTest.php`` file should look like this::
+
+    class Project_Tests_IntegrationTest extends Twig_Test_IntegrationTestCase
+    {
+        public function getExtensions()
+        {
+            return array(
+                new Project_Twig_Extension1(),
+                new Project_Twig_Extension2(),
+            );
+        }
+
+        public function getFixturesDir()
+        {
+            return dirname(__FILE__).'/Fixtures/';
+        }
+    }
+
+Fixtures examples can be found within the Twig repository
+`tests/Twig/Fixtures`_ directory.
+
+Node Tests
+~~~~~~~~~~
+
+Testing the node visitors can be complex, so extend your test cases from
+``Twig_Test_NodeTestCase``. Examples can be found in the Twig repository
+`tests/Twig/Node`_ directory.
+
+.. _`spl_autoload_register()`: http://www.php.net/spl_autoload_register
+.. _`rot13`:                   http://www.php.net/manual/en/function.str-rot13.php
+.. _`tests/Twig/Fixtures`:     https://github.com/fabpot/Twig/tree/master/test/Twig/Tests/Fixtures
+.. _`tests/Twig/Node`:         https://github.com/fabpot/Twig/tree/master/test/Twig/Tests/Node
diff --git a/vendor/twig/twig/doc/api.rst b/vendor/twig/twig/doc/api.rst
new file mode 100644 (file)
index 0000000..cbccb0f
--- /dev/null
@@ -0,0 +1,529 @@
+Twig for Developers
+===================
+
+This chapter describes the API to Twig and not the template language. It will
+be most useful as reference to those implementing the template interface to
+the application and not those who are creating Twig templates.
+
+Basics
+------
+
+Twig uses a central object called the **environment** (of class
+``Twig_Environment``). Instances of this class are used to store the
+configuration and extensions, and are used to load templates from the file
+system or other locations.
+
+Most applications will create one ``Twig_Environment`` object on application
+initialization and use that to load templates. In some cases it's however
+useful to have multiple environments side by side, if different configurations
+are in use.
+
+The simplest way to configure Twig to load templates for your application
+looks roughly like this::
+
+    require_once '/path/to/lib/Twig/Autoloader.php';
+    Twig_Autoloader::register();
+
+    $loader = new Twig_Loader_Filesystem('/path/to/templates');
+    $twig = new Twig_Environment($loader, array(
+        'cache' => '/path/to/compilation_cache',
+    ));
+
+This will create a template environment with the default settings and a loader
+that looks up the templates in the ``/path/to/templates/`` folder. Different
+loaders are available and you can also write your own if you want to load
+templates from a database or other resources.
+
+.. note::
+
+    Notice that the second argument of the environment is an array of options.
+    The ``cache`` option is a compilation cache directory, where Twig caches
+    the compiled templates to avoid the parsing phase for sub-sequent
+    requests. It is very different from the cache you might want to add for
+    the evaluated templates. For such a need, you can use any available PHP
+    cache library.
+
+To load a template from this environment you just have to call the
+``loadTemplate()`` method which then returns a ``Twig_Template`` instance::
+
+    $template = $twig->loadTemplate('index.html');
+
+To render the template with some variables, call the ``render()`` method::
+
+    echo $template->render(array('the' => 'variables', 'go' => 'here'));
+
+.. note::
+
+    The ``display()`` method is a shortcut to output the template directly.
+
+You can also load and render the template in one fell swoop::
+
+    echo $twig->render('index.html', array('the' => 'variables', 'go' => 'here'));
+
+.. _environment_options:
+
+Environment Options
+-------------------
+
+When creating a new ``Twig_Environment`` instance, you can pass an array of
+options as the constructor second argument::
+
+    $twig = new Twig_Environment($loader, array('debug' => true));
+
+The following options are available:
+
+* ``debug``: When set to ``true``, the generated templates have a
+  ``__toString()`` method that you can use to display the generated nodes
+  (default to ``false``).
+
+* ``charset``: The charset used by the templates (default to ``utf-8``).
+
+* ``base_template_class``: The base template class to use for generated
+  templates (default to ``Twig_Template``).
+
+* ``cache``: An absolute path where to store the compiled templates, or
+  ``false`` to disable caching (which is the default).
+
+* ``auto_reload``: When developing with Twig, it's useful to recompile the
+  template whenever the source code changes. If you don't provide a value for
+  the ``auto_reload`` option, it will be determined automatically based on the
+  ``debug`` value.
+
+* ``strict_variables``: If set to ``false``, Twig will silently ignore invalid
+  variables (variables and or attributes/methods that do not exist) and
+  replace them with a ``null`` value. When set to ``true``, Twig throws an
+  exception instead (default to ``false``).
+
+* ``autoescape``: If set to ``true``, auto-escaping will be enabled by default
+  for all templates (default to ``true``). As of Twig 1.8, you can set the
+  escaping strategy to use (``html``, ``js``, ``false`` to disable).
+  As of Twig 1.9, you can set the escaping strategy to use (``css``, ``url``, 
+  ``html_attr``, or a PHP callback that takes the template "filename" and must 
+  return the escaping strategy to use -- the callback cannot be a function name
+  to avoid collision with built-in escaping strategies).
+
+* ``optimizations``: A flag that indicates which optimizations to apply
+  (default to ``-1`` -- all optimizations are enabled; set it to ``0`` to
+  disable).
+
+Loaders
+-------
+
+Loaders are responsible for loading templates from a resource such as the file
+system.
+
+Compilation Cache
+~~~~~~~~~~~~~~~~~
+
+All template loaders can cache the compiled templates on the filesystem for
+future reuse. It speeds up Twig a lot as templates are only compiled once; and
+the performance boost is even larger if you use a PHP accelerator such as APC.
+See the ``cache`` and ``auto_reload`` options of ``Twig_Environment`` above
+for more information.
+
+Built-in Loaders
+~~~~~~~~~~~~~~~~
+
+Here is a list of the built-in loaders Twig provides:
+
+``Twig_Loader_Filesystem``
+..........................
+
+.. versionadded:: 1.10
+    The ``prependPath()`` and support for namespaces were added in Twig 1.10.
+
+``Twig_Loader_Filesystem`` loads templates from the file system. This loader
+can find templates in folders on the file system and is the preferred way to
+load them::
+
+    $loader = new Twig_Loader_Filesystem($templateDir);
+
+It can also look for templates in an array of directories::
+
+    $loader = new Twig_Loader_Filesystem(array($templateDir1, $templateDir2));
+
+With such a configuration, Twig will first look for templates in
+``$templateDir1`` and if they do not exist, it will fallback to look for them
+in the ``$templateDir2``.
+
+You can add or prepend paths via the ``addPath()`` and ``prependPath()``
+methods::
+
+    $loader->addPath($templateDir3);
+    $loader->prependPath($templateDir4);
+
+The filesystem loader also supports namespaced templates. This allows to group
+your templates under different namespaces which have their own template paths.
+
+When using the ``setPaths()``, ``addPath()``, and ``prependPath()`` methods,
+specify the namespace as the second argument (when not specified, these
+methods act on the "main" namespace)::
+
+    $loader->addPath($templateDir, 'admin');
+
+Namespaced templates can be accessed via the special
+``@namespace_name/template_path`` notation::
+
+    $twig->render('@admin/index.html', array());
+
+``Twig_Loader_String``
+......................
+
+``Twig_Loader_String`` loads templates from strings. It's a dummy loader as
+the template reference is the template source code::
+
+    $loader = new Twig_Loader_String();
+    $twig = new Twig_Environment($loader);
+
+    echo $twig->render('Hello {{ name }}!', array('name' => 'Fabien'));
+
+This loader should only be used for unit testing as it has severe limitations:
+several tags, like ``extends`` or ``include`` do not make sense to use as the
+reference to the template is the template source code itself.
+
+``Twig_Loader_Array``
+.....................
+
+``Twig_Loader_Array`` loads a template from a PHP array. It's passed an array
+of strings bound to template names::
+
+    $loader = new Twig_Loader_Array(array(
+        'index.html' => 'Hello {{ name }}!',
+    ));
+    $twig = new Twig_Environment($loader);
+
+    echo $twig->render('index.html', array('name' => 'Fabien'));
+
+This loader is very useful for unit testing. It can also be used for small
+projects where storing all templates in a single PHP file might make sense.
+
+.. tip::
+
+    When using the ``Array`` or ``String`` loaders with a cache mechanism, you
+    should know that a new cache key is generated each time a template content
+    "changes" (the cache key being the source code of the template). If you
+    don't want to see your cache grows out of control, you need to take care
+    of clearing the old cache file by yourself.
+
+``Twig_Loader_Chain``
+.....................
+
+``Twig_Loader_Chain`` delegates the loading of templates to other loaders::
+
+    $loader1 = new Twig_Loader_Array(array(
+        'base.html' => '{% block content %}{% endblock %}',
+    ));
+    $loader2 = new Twig_Loader_Array(array(
+        'index.html' => '{% extends "base.twig" %}{% block content %}Hello {{ name }}{% endblock %}',
+        'base.html'  => 'Will never be loaded',
+    ));
+
+    $loader = new Twig_Loader_Chain(array($loader1, $loader2));
+
+    $twig = new Twig_Environment($loader);
+
+When looking for a template, Twig will try each loader in turn and it will
+return as soon as the template is found. When rendering the ``index.html``
+template from the above example, Twig will load it with ``$loader2`` but the
+``base.html`` template will be loaded from ``$loader1``.
+
+``Twig_Loader_Chain`` accepts any loader that implements
+``Twig_LoaderInterface``.
+
+.. note::
+
+    You can also add loaders via the ``addLoader()`` method.
+
+Create your own Loader
+~~~~~~~~~~~~~~~~~~~~~~
+
+All loaders implement the ``Twig_LoaderInterface``::
+
+    interface Twig_LoaderInterface
+    {
+        /**
+         * Gets the source code of a template, given its name.
+         *
+         * @param  string $name string The name of the template to load
+         *
+         * @return string The template source code
+         */
+        function getSource($name);
+
+        /**
+         * Gets the cache key to use for the cache for a given template name.
+         *
+         * @param  string $name string The name of the template to load
+         *
+         * @return string The cache key
+         */
+        function getCacheKey($name);
+
+        /**
+         * Returns true if the template is still fresh.
+         *
+         * @param string    $name The template name
+         * @param timestamp $time The last modification time of the cached template
+         */
+        function isFresh($name, $time);
+    }
+
+As an example, here is how the built-in ``Twig_Loader_String`` reads::
+
+    class Twig_Loader_String implements Twig_LoaderInterface
+    {
+        public function getSource($name)
+        {
+          return $name;
+        }
+
+        public function getCacheKey($name)
+        {
+          return $name;
+        }
+
+        public function isFresh($name, $time)
+        {
+          return false;
+        }
+    }
+
+The ``isFresh()`` method must return ``true`` if the current cached template
+is still fresh, given the last modification time, or ``false`` otherwise.
+
+.. tip::
+
+    As of Twig 1.11.0, you can also implement ``Twig_ExistsLoaderInterface``
+    to make your loader faster when used with the chain loader.
+
+Using Extensions
+----------------
+
+Twig extensions are packages that add new features to Twig. Using an
+extension is as simple as using the ``addExtension()`` method::
+
+    $twig->addExtension(new Twig_Extension_Sandbox());
+
+Twig comes bundled with the following extensions:
+
+* *Twig_Extension_Core*: Defines all the core features of Twig.
+
+* *Twig_Extension_Escaper*: Adds automatic output-escaping and the possibility
+  to escape/unescape blocks of code.
+
+* *Twig_Extension_Sandbox*: Adds a sandbox mode to the default Twig
+  environment, making it safe to evaluate untrusted code.
+
+* *Twig_Extension_Optimizer*: Optimizes the node tree before compilation.
+
+The core, escaper, and optimizer extensions do not need to be added to the
+Twig environment, as they are registered by default.
+
+Built-in Extensions
+-------------------
+
+This section describes the features added by the built-in extensions.
+
+.. tip::
+
+    Read the chapter about extending Twig to learn how to create your own
+    extensions.
+
+Core Extension
+~~~~~~~~~~~~~~
+
+The ``core`` extension defines all the core features of Twig:
+
+* :doc:`Tags <tags/index>`;
+* :doc:`Filters <filters/index>`;
+* :doc:`Functions <functions/index>`;
+* :doc:`Tests <tests/index>`.
+
+Escaper Extension
+~~~~~~~~~~~~~~~~~
+
+The ``escaper`` extension adds automatic output escaping to Twig. It defines a
+tag, ``autoescape``, and a filter, ``raw``.
+
+When creating the escaper extension, you can switch on or off the global
+output escaping strategy::
+
+    $escaper = new Twig_Extension_Escaper('html');
+    $twig->addExtension($escaper);
+
+If set to ``html``, all variables in templates are escaped (using the ``html``
+escaping strategy), except those using the ``raw`` filter:
+
+.. code-block:: jinja
+
+    {{ article.to_html|raw }}
+
+You can also change the escaping mode locally by using the ``autoescape`` tag
+(see the :doc:`autoescape<tags/autoescape>` doc for the syntax used before
+Twig 1.8):
+
+.. code-block:: jinja
+
+    {% autoescape 'html' %}
+        {{ var }}
+        {{ var|raw }}      {# var won't be escaped #}
+        {{ var|escape }}   {# var won't be double-escaped #}
+    {% endautoescape %}
+
+.. warning::
+
+    The ``autoescape`` tag has no effect on included files.
+
+The escaping rules are implemented as follows:
+
+* Literals (integers, booleans, arrays, ...) used in the template directly as
+  variables or filter arguments are never automatically escaped:
+
+  .. code-block:: jinja
+
+        {{ "Twig<br />" }} {# won't be escaped #}
+
+        {% set text = "Twig<br />" %}
+        {{ text }} {# will be escaped #}
+
+* Expressions which the result is always a literal or a variable marked safe
+  are never automatically escaped:
+
+  .. code-block:: jinja
+
+        {{ foo ? "Twig<br />" : "<br />Twig" }} {# won't be escaped #}
+
+        {% set text = "Twig<br />" %}
+        {{ foo ? text : "<br />Twig" }} {# will be escaped #}
+
+        {% set text = "Twig<br />" %}
+        {{ foo ? text|raw : "<br />Twig" }} {# won't be escaped #}
+
+        {% set text = "Twig<br />" %}
+        {{ foo ? text|escape : "<br />Twig" }} {# the result of the expression won't be escaped #}
+
+* Escaping is applied before printing, after any other filter is applied:
+
+  .. code-block:: jinja
+
+        {{ var|upper }} {# is equivalent to {{ var|upper|escape }} #}
+
+* The `raw` filter should only be used at the end of the filter chain:
+
+  .. code-block:: jinja
+
+        {{ var|raw|upper }} {# will be escaped #}
+
+        {{ var|upper|raw }} {# won't be escaped #}
+
+* Automatic escaping is not applied if the last filter in the chain is marked
+  safe for the current context (e.g. ``html`` or ``js``). ``escaper`` and
+  ``escaper('html')`` are marked safe for html, ``escaper('js')`` is marked
+  safe for javascript, ``raw`` is marked safe for everything.
+
+  .. code-block:: jinja
+
+        {% autoescape 'js' %}
+            {{ var|escape('html') }} {# will be escaped for html and javascript #}
+            {{ var }} {# will be escaped for javascript #}
+            {{ var|escape('js') }} {# won't be double-escaped #}
+        {% endautoescape %}
+
+.. note::
+
+    Note that autoescaping has some limitations as escaping is applied on
+    expressions after evaluation. For instance, when working with
+    concatenation, ``{{ foo|raw ~ bar }}`` won't give the expected result as
+    escaping is applied on the result of the concatenation, not on the
+    individual variables (so, the ``raw`` filter won't have any effect here).
+
+Sandbox Extension
+~~~~~~~~~~~~~~~~~
+
+The ``sandbox`` extension can be used to evaluate untrusted code. Access to
+unsafe attributes and methods is prohibited. The sandbox security is managed
+by a policy instance. By default, Twig comes with one policy class:
+``Twig_Sandbox_SecurityPolicy``. This class allows you to white-list some
+tags, filters, properties, and methods::
+
+    $tags = array('if');
+    $filters = array('upper');
+    $methods = array(
+        'Article' => array('getTitle', 'getBody'),
+    );
+    $properties = array(
+        'Article' => array('title', 'body'),
+    );
+    $functions = array('range');
+    $policy = new Twig_Sandbox_SecurityPolicy($tags, $filters, $methods, $properties, $functions);
+
+With the previous configuration, the security policy will only allow usage of
+the ``if`` tag, and the ``upper`` filter. Moreover, the templates will only be
+able to call the ``getTitle()`` and ``getBody()`` methods on ``Article``
+objects, and the ``title`` and ``body`` public properties. Everything else
+won't be allowed and will generate a ``Twig_Sandbox_SecurityError`` exception.
+
+The policy object is the first argument of the sandbox constructor::
+
+    $sandbox = new Twig_Extension_Sandbox($policy);
+    $twig->addExtension($sandbox);
+
+By default, the sandbox mode is disabled and should be enabled when including
+untrusted template code by using the ``sandbox`` tag:
+
+.. code-block:: jinja
+
+    {% sandbox %}
+        {% include 'user.html' %}
+    {% endsandbox %}
+
+You can sandbox all templates by passing ``true`` as the second argument of
+the extension constructor::
+
+    $sandbox = new Twig_Extension_Sandbox($policy, true);
+
+Optimizer Extension
+~~~~~~~~~~~~~~~~~~~
+
+The ``optimizer`` extension optimizes the node tree before compilation::
+
+    $twig->addExtension(new Twig_Extension_Optimizer());
+
+By default, all optimizations are turned on. You can select the ones you want
+to enable by passing them to the constructor::
+
+    $optimizer = new Twig_Extension_Optimizer(Twig_NodeVisitor_Optimizer::OPTIMIZE_FOR);
+
+    $twig->addExtension($optimizer);
+
+Twig supports the following optimizations:
+
+* ``Twig_NodeVisitor_Optimizer::OPTIMIZE_ALL``, enables all optimizations
+  (this is the default value).
+* ``Twig_NodeVisitor_Optimizer::OPTIMIZE_NONE``, disables all optimizations.
+  This reduces the compilation time, but it can increase the execution time
+  and the consumed memory.
+* ``Twig_NodeVisitor_Optimizer::OPTIMIZE_FOR``, optimizes the ``for`` tag by
+  removing the ``loop`` variable creation whenever possible.
+* ``Twig_NodeVisitor_Optimizer::OPTIMIZE_RAW_FILTER``, removes the ``raw``
+  filter whenever possible.
+* ``Twig_NodeVisitor_Optimizer::OPTIMIZE_VAR_ACCESS``, simplifies the creation
+  and access of variables in the compiled templates whenever possible.
+
+Exceptions
+----------
+
+Twig can throw exceptions:
+
+* ``Twig_Error``: The base exception for all errors.
+
+* ``Twig_Error_Syntax``: Thrown to tell the user that there is a problem with
+  the template syntax.
+
+* ``Twig_Error_Runtime``: Thrown when an error occurs at runtime (when a filter
+  does not exist for instance).
+
+* ``Twig_Error_Loader``: Thrown when an error occurs during template loading.
+
+* ``Twig_Sandbox_SecurityError``: Thrown when an unallowed tag, filter, or
+  method is called in a sandboxed template.
diff --git a/vendor/twig/twig/doc/coding_standards.rst b/vendor/twig/twig/doc/coding_standards.rst
new file mode 100644 (file)
index 0000000..e0aab35
--- /dev/null
@@ -0,0 +1,101 @@
+Coding Standards
+================
+
+When writing Twig templates, we recommend you to follow these official coding
+standards:
+
+* Put one (and only one) space after the start of a delimiter (``{{``, ``{%``,
+  and ``{#``) and before the end of a delimiter (``}}``, ``%}``, and ``#}``):
+
+  .. code-block:: jinja
+
+    {{ foo }}
+    {# comment #}
+    {% if foo %}{% endif %}
+
+  When using the whitespace control character, do not put any spaces between
+  it and the delimiter:
+
+  .. code-block:: jinja
+
+    {{- foo -}}
+    {#- comment -#}
+    {%- if foo -%}{%- endif -%}
+
+* Put one (and only one) space before and after the following operators:
+  comparison operators (``==``, ``!=``, ``<``, ``>``, ``>=``, ``<=``), math
+  operators (``+``, ``-``, ``/``, ``*``, ``%``, ``//``, ``**``), logic
+  operators (``not``, ``and``, ``or``), ``~``, ``is``, ``in``, and the ternary
+  operator (``?:``):
+
+  .. code-block:: jinja
+
+     {{ 1 + 2 }}
+     {{ foo ~ bar }}
+     {{ true ? true : false }}
+
+* Put one (and only one) space after the ``:`` sign in hashes and ``,`` in
+  arrays and hashes:
+
+  .. code-block:: jinja
+
+     {{ [1, 2, 3] }}
+     {{ {'foo': 'bar'} }}
+
+* Do not put any spaces after an opening parenthesis and before a closing
+  parenthesis in expressions:
+
+  .. code-block:: jinja
+
+    {{ 1 + (2 * 3) }}
+
+* Do not put any spaces before and after string delimiters:
+
+  .. code-block:: jinja
+
+    {{ 'foo' }}
+    {{ "foo" }}
+
+* Do not put any spaces before and after the following operators: ``|``,
+  ``.``, ``..``, ``[]``:
+
+  .. code-block:: jinja
+
+    {{ foo|upper|lower }}
+    {{ user.name }}
+    {{ user[name] }}
+    {% for i in 1..12 %}{% endfor %}
+
+* Do not put any spaces before and after the parenthesis used for filter and
+  function calls:
+
+  .. code-block:: jinja
+
+     {{ foo|default('foo') }}
+     {{ range(1..10) }}
+
+* Do not put any spaces before and after the opening and the closing of arrays
+  and hashes:
+
+  .. code-block:: jinja
+
+     {{ [1, 2, 3] }}
+     {{ {'foo': 'bar'} }}
+
+* Use lower cased and underscored variable names:
+
+  .. code-block:: jinja
+
+     {% set foo = 'foo' %}
+     {% set foo_bar = 'foo' %}
+
+* Indent your code inside tags (use the same indentation as the one used for
+  the main language of the file):
+
+  .. code-block:: jinja
+
+     {% block foo %}
+        {% if true %}
+            true
+        {% endif %}
+     {% endblock %}
diff --git a/vendor/twig/twig/doc/deprecated.rst b/vendor/twig/twig/doc/deprecated.rst
new file mode 100644 (file)
index 0000000..f0a3a0f
--- /dev/null
@@ -0,0 +1,98 @@
+Deprecated Features
+===================
+
+This document lists all deprecated features in Twig. Deprecated features are
+kept for backward compatibility and removed in the next major release (a
+feature that was deprecated in Twig 1.x is removed in Twig 2.0).
+
+Token Parsers
+-------------
+
+* As of Twig 1.x, the token parser broker sub-system is deprecated. The
+  following class and interface will be removed in 2.0:
+
+  * ``Twig_TokenParserBrokerInterface``
+  * ``Twig_TokenParserBroker``
+
+Extensions
+----------
+
+* As of Twig 1.x, the ability to remove an extension is deprecated and the
+  ``Twig_Environment::removeExtension()`` method will be removed in 2.0.
+
+PEAR
+----
+
+PEAR support will be discontinued in Twig 2.0, and no PEAR packages will be
+provided. Use Composer instead.
+
+Filters
+-------
+
+* As of Twig 1.x, use ``Twig_SimpleFilter`` to add a filter. The following
+  classes and interfaces will be removed in 2.0:
+
+  * ``Twig_FilterInterface``
+  * ``Twig_FilterCallableInterface``
+  * ``Twig_Filter``
+  * ``Twig_Filter_Function``
+  * ``Twig_Filter_Method``
+  * ``Twig_Filter_Node``
+
+* As of Twig 2.x, the ``Twig_SimpleFilter`` class is deprecated and will be
+  removed in Twig 3.x (use ``Twig_Filter`` instead). In Twig 2.x,
+  ``Twig_SimpleFilter`` is just an alias for ``Twig_Filter``.
+
+Functions
+---------
+
+* As of Twig 1.x, use ``Twig_SimpleFunction`` to add a function. The following
+  classes and interfaces will be removed in 2.0:
+
+  * ``Twig_FunctionInterface``
+  * ``Twig_FunctionCallableInterface``
+  * ``Twig_Function``
+  * ``Twig_Function_Function``
+  * ``Twig_Function_Method``
+  * ``Twig_Function_Node``
+
+* As of Twig 2.x, the ``Twig_SimpleFunction`` class is deprecated and will be
+  removed in Twig 3.x (use ``Twig_Function`` instead). In Twig 2.x,
+  ``Twig_SimpleFunction`` is just an alias for ``Twig_Function``.
+
+Tests
+-----
+
+* As of Twig 1.x, use ``Twig_SimpleTest`` to add a test. The following classes
+  and interfaces will be removed in 2.0:
+
+  * ``Twig_TestInterface``
+  * ``Twig_TestCallableInterface``
+  * ``Twig_Test``
+  * ``Twig_Test_Function``
+  * ``Twig_Test_Method``
+  * ``Twig_Test_Node``
+
+* As of Twig 2.x, the ``Twig_SimpleTest`` class is deprecated and will be
+  removed in Twig 3.x (use ``Twig_Test`` instead). In Twig 2.x,
+  ``Twig_SimpleTest`` is just an alias for ``Twig_Test``.
+
+Interfaces
+----------
+
+* As of Twig 2.x, the following interfaces are deprecated and empty (they will
+  be removed in Twig 3.0):
+
+* ``Twig_CompilerInterface``     (use ``Twig_Compiler`` instead)
+* ``Twig_LexerInterface``        (use ``Twig_Lexer`` instead)
+* ``Twig_NodeInterface``         (use ``Twig_Node`` instead)
+* ``Twig_ParserInterface``       (use ``Twig_Parser`` instead)
+* ``Twig_ExistsLoaderInterface`` (merged with ``Twig_LoaderInterface``)
+* ``Twig_TemplateInterface``     (use ``Twig_Template`` instead)
+
+Globals
+-------
+
+* As of Twig 2.x, the ability to register a global variable after the runtime
+  or the extensions have been initialized is not possible anymore (but
+  changing the value of an already registered global is possible).
diff --git a/vendor/twig/twig/doc/filters/abs.rst b/vendor/twig/twig/doc/filters/abs.rst
new file mode 100644 (file)
index 0000000..3a82f62
--- /dev/null
@@ -0,0 +1,18 @@
+``abs``
+=======
+
+The ``abs`` filter returns the absolute value.
+
+.. code-block:: jinja
+
+    {# number = -5 #}
+    
+    {{ number|abs }}
+    
+    {# outputs 5 #}
+
+.. note::
+
+    Internally, Twig uses the PHP `abs`_ function.
+
+.. _`abs`: http://php.net/abs
diff --git a/vendor/twig/twig/doc/filters/batch.rst b/vendor/twig/twig/doc/filters/batch.rst
new file mode 100644 (file)
index 0000000..4366b57
--- /dev/null
@@ -0,0 +1,45 @@
+``batch``
+=========
+
+.. versionadded:: 1.12.3
+    The batch filter was added in Twig 1.12.3.
+
+The ``batch`` filter "batches" items by returning a list of lists with the
+given number of items. If you provide a second parameter, it is used to fill
+missing items:
+
+.. code-block:: jinja
+
+    {% set items = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] %}
+
+    <table>
+    {% for row in items|batch(3, 'No item') %}
+      <tr>
+      {% for column in row %}
+        <td>{{ column }}</td>
+      {% endfor %}
+      </tr>
+    {% endfor %}
+    </table>
+
+The above example will be rendered as:
+
+.. code-block:: jinja
+
+    <table>
+      <tr>
+          <td>a</td>
+          <td>b</td>
+          <td>c</td>
+        </tr>
+      <tr>
+          <td>d</td>
+          <td>e</td>
+          <td>f</td>
+        </tr>
+      <tr>
+          <td>g</td>
+          <td>No item</td>
+          <td>No item</td>
+        </tr>
+    </table>
diff --git a/vendor/twig/twig/doc/filters/capitalize.rst b/vendor/twig/twig/doc/filters/capitalize.rst
new file mode 100644 (file)
index 0000000..10546a1
--- /dev/null
@@ -0,0 +1,11 @@
+``capitalize``
+==============
+
+The ``capitalize`` filter capitalizes a value. The first character will be
+uppercase, all others lowercase:
+
+.. code-block:: jinja
+
+    {{ 'my first car'|capitalize }}
+
+    {# outputs 'My first car' #}
diff --git a/vendor/twig/twig/doc/filters/convert_encoding.rst b/vendor/twig/twig/doc/filters/convert_encoding.rst
new file mode 100644 (file)
index 0000000..1b0eb60
--- /dev/null
@@ -0,0 +1,28 @@
+``convert_encoding``
+====================
+
+.. versionadded:: 1.4
+    The ``convert_encoding`` filter was added in Twig 1.4.
+
+The ``convert_encoding`` filter converts a string from one encoding to
+another. The first argument is the expected output charset and the second one
+is the input charset:
+
+.. code-block:: jinja
+
+    {{ data|convert_encoding('UTF-8', 'iso-2022-jp') }}
+
+.. note::
+
+    This filter relies on the `iconv`_ or `mbstring`_ extension, so one of
+    them must be installed. In case both are installed, `mbstring`_ is used by
+    default (Twig before 1.8.1 uses `iconv`_ by default).
+
+Arguments
+---------
+
+ * ``from``: The input charset
+ * ``to``:   The output charset
+
+.. _`iconv`:    http://php.net/iconv
+.. _`mbstring`: http://php.net/mbstring
diff --git a/vendor/twig/twig/doc/filters/date.rst b/vendor/twig/twig/doc/filters/date.rst
new file mode 100644 (file)
index 0000000..8e2f31f
--- /dev/null
@@ -0,0 +1,88 @@
+``date``
+========
+
+.. versionadded:: 1.1
+    The timezone support has been added in Twig 1.1.
+
+.. versionadded:: 1.5
+    The default date format support has been added in Twig 1.5.
+
+.. versionadded:: 1.6.1
+    The default timezone support has been added in Twig 1.6.1.
+
+.. versionadded:: 1.11.0
+    The introduction of the false value for the timezone was introduced in Twig 1.11.0
+
+The ``date`` filter formats a date to a given format:
+
+.. code-block:: jinja
+
+    {{ post.published_at|date("m/d/Y") }}
+
+The ``date`` filter accepts strings (it must be in a format supported by the
+`strtotime`_ function), `DateTime`_ instances, or `DateInterval`_ instances. For
+instance, to display the current date, filter the word "now":
+
+.. code-block:: jinja
+
+    {{ "now"|date("m/d/Y") }}
+
+To escape words and characters in the date format use ``\\`` in front of each
+character:
+
+.. code-block:: jinja
+
+    {{ post.published_at|date("F jS \\a\\t g:ia") }}
+
+If the value passed to the ``date`` filter is ``null``, it will return the
+current date by default. If an empty string is desired instead of the current
+date, use a ternary operator:
+
+.. code-block:: jinja
+
+    {{ post.published_at is empty ? "" : post.published_at|date("m/d/Y") }}
+
+If no format is provided, Twig will use the default one: ``F j, Y H:i``. This
+default can be easily changed by calling the ``setDateFormat()`` method on the
+``core`` extension instance. The first argument is the default format for
+dates and the second one is the default format for date intervals:
+
+.. code-block:: php
+
+    $twig = new Twig_Environment($loader);
+    $twig->getExtension('core')->setDateFormat('d/m/Y', '%d days');
+
+Timezone
+--------
+
+By default, the date is displayed by applying the default timezone (the one
+specified in php.ini or declared in Twig -- see below), but you can override
+it by explicitly specifying a timezone:
+
+.. code-block:: jinja
+
+    {{ post.published_at|date("m/d/Y", "Europe/Paris") }}
+
+If the date is already a DateTime object, and if you want to keep its current
+timezone, pass ``false`` as the timezone value:
+
+.. code-block:: jinja
+
+    {{ post.published_at|date("m/d/Y", false) }}
+
+The default timezone can also be set globally by calling ``setTimezone()``:
+
+.. code-block:: php
+
+    $twig = new Twig_Environment($loader);
+    $twig->getExtension('core')->setTimezone('Europe/Paris');
+
+Arguments
+---------
+
+ * ``format``:   The date format
+ * ``timezone``: The date timezone
+
+.. _`strtotime`:    http://www.php.net/strtotime
+.. _`DateTime`:     http://www.php.net/DateTime
+.. _`DateInterval`: http://www.php.net/DateInterval
diff --git a/vendor/twig/twig/doc/filters/date_modify.rst b/vendor/twig/twig/doc/filters/date_modify.rst
new file mode 100644 (file)
index 0000000..6a5c73d
--- /dev/null
@@ -0,0 +1,23 @@
+``date_modify``
+===============
+
+.. versionadded:: 1.9.0
+    The date_modify filter has been added in Twig 1.9.0.
+
+The ``date_modify`` filter modifies a date with a given modifier string:
+
+.. code-block:: jinja
+
+    {{ post.published_at|date_modify("+1 day")|date("m/d/Y") }}
+
+The ``date_modify`` filter accepts strings (it must be in a format supported
+by the `strtotime`_ function) or `DateTime`_ instances. You can easily combine
+it with the :doc:`date<date>` filter for formatting.
+
+Arguments
+---------
+
+ * ``modifier``: The modifier
+
+.. _`strtotime`: http://www.php.net/strtotime
+.. _`DateTime`:  http://www.php.net/DateTime
diff --git a/vendor/twig/twig/doc/filters/default.rst b/vendor/twig/twig/doc/filters/default.rst
new file mode 100644 (file)
index 0000000..46ed963
--- /dev/null
@@ -0,0 +1,33 @@
+``default``
+===========
+
+The ``default`` filter returns the passed default value if the value is
+undefined or empty, otherwise the value of the variable:
+
+.. code-block:: jinja
+
+    {{ var|default('var is not defined') }}
+
+    {{ var.foo|default('foo item on var is not defined') }}
+
+    {{ var['foo']|default('foo item on var is not defined') }}
+
+    {{ ''|default('passed var is empty')  }}
+
+When using the ``default`` filter on an expression that uses variables in some
+method calls, be sure to use the ``default`` filter whenever a variable can be
+undefined:
+
+.. code-block:: jinja
+
+    {{ var.method(foo|default('foo'))|default('foo') }}
+
+.. note::
+
+    Read the documentation for the :doc:`defined<../tests/defined>` and
+    :doc:`empty<../tests/empty>` tests to learn more about their semantics.
+
+Arguments
+---------
+
+ * ``default``: The default value
diff --git a/vendor/twig/twig/doc/filters/escape.rst b/vendor/twig/twig/doc/filters/escape.rst
new file mode 100644 (file)
index 0000000..5ade7d7
--- /dev/null
@@ -0,0 +1,93 @@
+``escape``
+==========
+
+.. versionadded:: 1.9.0
+    The ``css``, ``url``, and ``html_attr`` strategies were added in Twig
+    1.9.0.
+
+The ``escape`` filter escapes a string for safe insertion into the final
+output. It supports different escaping strategies depending on the template
+context.
+
+By default, it uses the HTML escaping strategy:
+
+.. code-block:: jinja
+
+    {{ user.username|escape }}
+
+For convenience, the ``e`` filter is defined as an alias:
+
+.. code-block:: jinja
+
+    {{ user.username|e }}
+
+The ``escape`` filter can also be used in other contexts than HTML thanks to
+an optional argument which defines the escaping strategy to use:
+
+.. code-block:: jinja
+
+    {{ user.username|e }}
+    {# is equivalent to #}
+    {{ user.username|e('html') }}
+
+And here is how to escape variables included in JavaScript code:
+
+.. code-block:: jinja
+
+    {{ user.username|escape('js') }}
+    {{ user.username|e('js') }}
+
+The ``escape`` filter supports the following escaping strategies:
+
+* ``html``: escapes a string for the **HTML body** context.
+
+* ``js``: escapes a string for the **JavaScript context**.
+
+* ``css``: escapes a string for the **CSS context**. CSS escaping can be
+  applied to any string being inserted into CSS and escapes everything except
+  alphanumerics.
+
+* ``url``: escapes a string for the **URI or parameter contexts**. This should
+  not be used to escape an entire URI; only a subcomponent being inserted.
+
+* ``html_attr``: escapes a string for the **HTML attribute** context.
+
+.. note::
+
+    Internally, ``escape`` uses the PHP native `htmlspecialchars`_ function
+    for the HTML escaping strategy.
+
+.. caution::
+
+    When using automatic escaping, Twig tries to not double-escape a variable
+    when the automatic escaping strategy is the same as the one applied by the
+    escape filter; but that does not work when using a variable as the
+    escaping strategy:
+
+    .. code-block:: jinja
+
+        {% set strategy = 'html' %}
+
+        {% autoescape 'html' %}
+            {{ var|escape('html') }}   {# won't be double-escaped #}
+            {{ var|escape(strategy) }} {# will be double-escaped #}
+        {% endautoescape %}
+
+    When using a variable as the escaping strategy, you should disable
+    automatic escaping:
+
+    .. code-block:: jinja
+
+        {% set strategy = 'html' %}
+
+        {% autoescape 'html' %}
+            {{ var|escape(strategy)|raw }} {# won't be double-escaped #}
+        {% endautoescape %}
+
+Arguments
+---------
+
+ * ``strategy``: The escaping strategy
+ * ``charset``:  The string charset
+
+.. _`htmlspecialchars`: http://php.net/htmlspecialchars
diff --git a/vendor/twig/twig/doc/filters/first.rst b/vendor/twig/twig/doc/filters/first.rst
new file mode 100644 (file)
index 0000000..4295e83
--- /dev/null
@@ -0,0 +1,25 @@
+``first``
+=========
+
+.. versionadded:: 1.12.2
+    The first filter was added in Twig 1.12.2.
+
+The ``first`` filter returns the first "element" of a sequence, a mapping, or
+a string:
+
+.. code-block:: jinja
+
+    {{ [1, 2, 3, 4]|first }}
+    {# outputs 1 #}
+
+    {{ { a: 1, b: 2, c: 3, d: 4 }|first }}
+    {# outputs 1 #}
+
+    {{ '1234'|first }}
+    {# outputs 1 #}
+
+.. note::
+
+    It also works with objects implementing the `Traversable`_ interface.
+
+.. _`Traversable`: http://php.net/manual/en/class.traversable.php
diff --git a/vendor/twig/twig/doc/filters/format.rst b/vendor/twig/twig/doc/filters/format.rst
new file mode 100644 (file)
index 0000000..fe55a09
--- /dev/null
@@ -0,0 +1,16 @@
+``format``
+==========
+
+The ``format`` filter formats a given string by replacing the placeholders
+(placeholders follows the `sprintf`_ notation):
+
+.. code-block:: jinja
+
+    {{ "I like %s and %s."|format(foo, "bar") }}
+
+    {# returns I like foo and bar
+       if the foo parameter equals to the foo string. #}
+
+.. _`sprintf`: http://www.php.net/sprintf
+
+.. seealso:: :doc:`replace<replace>`
diff --git a/vendor/twig/twig/doc/filters/index.rst b/vendor/twig/twig/doc/filters/index.rst
new file mode 100644 (file)
index 0000000..b0c6b38
--- /dev/null
@@ -0,0 +1,36 @@
+Filters
+=======
+
+.. toctree::
+    :maxdepth: 1
+
+    abs
+    batch
+    capitalize
+    convert_encoding
+    date
+    date_modify
+    default
+    escape
+    first
+    format
+    join
+    json_encode
+    keys
+    last
+    length
+    lower
+    nl2br
+    number_format
+    merge
+    upper
+    raw
+    replace
+    reverse
+    slice
+    sort
+    split
+    striptags
+    title
+    trim
+    url_encode
diff --git a/vendor/twig/twig/doc/filters/join.rst b/vendor/twig/twig/doc/filters/join.rst
new file mode 100644 (file)
index 0000000..f495242
--- /dev/null
@@ -0,0 +1,23 @@
+``join``
+========
+
+The ``join`` filter returns a string which is the concatenation of the items
+of a sequence:
+
+.. code-block:: jinja
+
+    {{ [1, 2, 3]|join }}
+    {# returns 123 #}
+
+The separator between elements is an empty string per default, but you can
+define it with the optional first parameter:
+
+.. code-block:: jinja
+
+    {{ [1, 2, 3]|join('|') }}
+    {# returns 1|2|3 #}
+
+Arguments
+---------
+
+ * ``glue``: The separator
diff --git a/vendor/twig/twig/doc/filters/json_encode.rst b/vendor/twig/twig/doc/filters/json_encode.rst
new file mode 100644 (file)
index 0000000..a33fef1
--- /dev/null
@@ -0,0 +1,21 @@
+``json_encode``
+===============
+
+The ``json_encode`` filter returns the JSON representation of a string:
+
+.. code-block:: jinja
+
+    {{ data|json_encode() }}
+
+.. note::
+
+    Internally, Twig uses the PHP `json_encode`_ function.
+
+Arguments
+---------
+
+ * ``options``: A bitmask of `json_encode options`_ (``{{
+   data|json_encode(constant(JSON_PRETTY_PRINT)) }}``)
+
+.. _`json_encode`: http://php.net/json_encode
+.. _`json_encode options`: http://www.php.net/manual/en/json.constants.php
diff --git a/vendor/twig/twig/doc/filters/keys.rst b/vendor/twig/twig/doc/filters/keys.rst
new file mode 100644 (file)
index 0000000..e4f090c
--- /dev/null
@@ -0,0 +1,11 @@
+``keys``
+========
+
+The ``keys`` filter returns the keys of an array. It is useful when you want to
+iterate over the keys of an array:
+
+.. code-block:: jinja
+
+    {% for key in array|keys %}
+        ...
+    {% endfor %}
diff --git a/vendor/twig/twig/doc/filters/last.rst b/vendor/twig/twig/doc/filters/last.rst
new file mode 100644 (file)
index 0000000..723c0b5
--- /dev/null
@@ -0,0 +1,25 @@
+``last``
+========
+
+.. versionadded:: 1.12.2
+    The last filter was added in Twig 1.12.2.
+
+The ``last`` filter returns the last "element" of a sequence, a mapping, or
+a string:
+
+.. code-block:: jinja
+
+    {{ [1, 2, 3, 4]|last }}
+    {# outputs 4 #}
+
+    {{ { a: 1, b: 2, c: 3, d: 4 }|last }}
+    {# outputs 4 #}
+
+    {{ '1234'|last }}
+    {# outputs 4 #}
+
+.. note::
+
+    It also works with objects implementing the `Traversable`_ interface.
+
+.. _`Traversable`: http://php.net/manual/en/class.traversable.php
diff --git a/vendor/twig/twig/doc/filters/length.rst b/vendor/twig/twig/doc/filters/length.rst
new file mode 100644 (file)
index 0000000..f79b9bd
--- /dev/null
@@ -0,0 +1,12 @@
+``length``
+==========
+
+The ``length`` filters returns the number of items of a sequence or mapping, or
+the length of a string:
+
+.. code-block:: jinja
+
+    {% if users|length > 10 %}
+        ...
+    {% endif %}
+
diff --git a/vendor/twig/twig/doc/filters/lower.rst b/vendor/twig/twig/doc/filters/lower.rst
new file mode 100644 (file)
index 0000000..ef9faa9
--- /dev/null
@@ -0,0 +1,10 @@
+``lower``
+=========
+
+The ``lower`` filter converts a value to lowercase:
+
+.. code-block:: jinja
+
+    {{ 'WELCOME'|lower }}
+
+    {# outputs 'welcome' #}
diff --git a/vendor/twig/twig/doc/filters/merge.rst b/vendor/twig/twig/doc/filters/merge.rst
new file mode 100644 (file)
index 0000000..05a2ae7
--- /dev/null
@@ -0,0 +1,41 @@
+``merge``
+=========
+
+The ``merge`` filter merges an array with another array:
+
+.. code-block:: jinja
+
+    {% set values = [1, 2] %}
+
+    {% set values = values|merge(['apple', 'orange']) %}
+
+    {# values now contains [1, 2, 'apple', 'orange'] #}
+
+New values are added at the end of the existing ones.
+
+The ``merge`` filter also works on hashes:
+
+.. code-block:: jinja
+
+    {% set items = { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'unknown' } %}
+
+    {% set items = items|merge({ 'peugeot': 'car', 'renault': 'car' }) %}
+
+    {# items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car', 'renault': 'car' } #}
+
+For hashes, the merging process occurs on the keys: if the key does not
+already exist, it is added but if the key already exists, its value is
+overridden.
+
+.. tip::
+
+    If you want to ensure that some values are defined in an array (by given
+    default values), reverse the two elements in the call:
+
+    .. code-block:: jinja
+
+        {% set items = { 'apple': 'fruit', 'orange': 'fruit' } %}
+
+        {% set items = { 'apple': 'unknown' }|merge(items) %}
+
+        {# items now contains { 'apple': 'fruit', 'orange': 'fruit' } #}
diff --git a/vendor/twig/twig/doc/filters/nl2br.rst b/vendor/twig/twig/doc/filters/nl2br.rst
new file mode 100644 (file)
index 0000000..694c672
--- /dev/null
@@ -0,0 +1,22 @@
+``nl2br``
+=========
+
+.. versionadded:: 1.5
+    The nl2br filter was added in Twig 1.5.
+
+The ``nl2br`` filter inserts HTML line breaks before all newlines in a string:
+
+.. code-block:: jinja
+
+    {{ "I like Twig.\nYou will like it too."|nl2br }}
+    {# outputs
+
+        I like Twig.<br />
+        You will like it too.
+
+    #}
+
+.. note::
+
+    The ``nl2br`` filter pre-escapes the input before applying the
+    transformation.
diff --git a/vendor/twig/twig/doc/filters/number_format.rst b/vendor/twig/twig/doc/filters/number_format.rst
new file mode 100644 (file)
index 0000000..fedacd9
--- /dev/null
@@ -0,0 +1,45 @@
+``number_format``
+=================
+
+.. versionadded:: 1.5
+    The number_format filter was added in Twig 1.5
+
+The ``number_format`` filter formats numbers.  It is a wrapper around PHP's
+`number_format`_ function:
+
+.. code-block:: jinja
+
+    {{ 200.35|number_format }}
+
+You can control the number of decimal places, decimal point, and thousands
+separator using the additional arguments:
+
+.. code-block:: jinja
+
+    {{ 9800.333|number_format(2, '.', ',') }}
+
+If no formatting options are provided then Twig will use the default formatting
+options of:
+
+- 0 decimal places.
+- ``.`` as the decimal point.
+- ``,`` as the thousands separator.
+
+These defaults can be easily changed through the core extension:
+
+.. code-block:: php
+
+    $twig = new Twig_Environment($loader);
+    $twig->getExtension('core')->setNumberFormat(3, '.', ',');
+
+The defaults set for ``number_format`` can be over-ridden upon each call using the
+additional parameters.
+
+Arguments
+---------
+
+ * ``decimal``:       The number of decimal points to display
+ * ``decimal_point``: The character(s) to use for the decimal point
+ * ``decimal_sep``:   The character(s) to use for the thousands separator
+
+.. _`number_format`: http://php.net/number_format
diff --git a/vendor/twig/twig/doc/filters/raw.rst b/vendor/twig/twig/doc/filters/raw.rst
new file mode 100644 (file)
index 0000000..434dd24
--- /dev/null
@@ -0,0 +1,12 @@
+``raw``
+=======
+
+The ``raw`` filter marks the value as being "safe", which means that in an
+environment with automatic escaping enabled this variable will not be escaped
+if ``raw`` is the last filter applied to it:
+
+.. code-block:: jinja
+
+    {% autoescape true %}
+        {{ var|raw }} {# var won't be escaped #}
+    {% endautoescape %}
diff --git a/vendor/twig/twig/doc/filters/replace.rst b/vendor/twig/twig/doc/filters/replace.rst
new file mode 100644 (file)
index 0000000..e961f23
--- /dev/null
@@ -0,0 +1,19 @@
+``replace``
+===========
+
+The ``replace`` filter formats a given string by replacing the placeholders
+(placeholders are free-form):
+
+.. code-block:: jinja
+
+    {{ "I like %this% and %that%."|replace({'%this%': foo, '%that%': "bar"}) }}
+
+    {# returns I like foo and bar
+       if the foo parameter equals to the foo string. #}
+
+Arguments
+---------
+
+ * ``replace_pairs``: The placeholder values
+
+.. seealso:: :doc:`format<format>`
diff --git a/vendor/twig/twig/doc/filters/reverse.rst b/vendor/twig/twig/doc/filters/reverse.rst
new file mode 100644 (file)
index 0000000..752192b
--- /dev/null
@@ -0,0 +1,47 @@
+``reverse``
+===========
+
+.. versionadded:: 1.6
+    Support for strings has been added in Twig 1.6.
+
+The ``reverse`` filter reverses a sequence, a mapping, or a string:
+
+.. code-block:: jinja
+
+    {% for user in users|reverse %}
+        ...
+    {% endfor %}
+
+    {{ '1234'|reverse }}
+
+    {# outputs 4321 #}
+
+.. tip::
+
+    For sequences and mappings, numeric keys are not preserved. To reverse
+    them as well, pass ``true`` as an argument to the ``reverse`` filter:
+
+    .. code-block:: jinja
+
+        {% for key, value in {1: "a", 2: "b", 3: "c"}|reverse %}
+            {{ key }}: {{ value }}
+        {%- endfor %}
+
+        {# output: 0: c    1: b    2: a #}
+
+        {% for key, value in {1: "a", 2: "b", 3: "c"}|reverse(true) %}
+            {{ key }}: {{ value }}
+        {%- endfor %}
+
+        {# output: 3: c    2: b    1: a #}
+
+.. note::
+
+    It also works with objects implementing the `Traversable`_ interface.
+
+Arguments
+---------
+
+ * ``preserve_keys``: Preserve keys when reversing a mapping or a sequence.
+
+.. _`Traversable`: http://php.net/Traversable
diff --git a/vendor/twig/twig/doc/filters/slice.rst b/vendor/twig/twig/doc/filters/slice.rst
new file mode 100644 (file)
index 0000000..dbd5db3
--- /dev/null
@@ -0,0 +1,70 @@
+``slice``
+===========
+
+.. versionadded:: 1.6
+    The slice filter was added in Twig 1.6.
+
+The ``slice`` filter extracts a slice of a sequence, a mapping, or a string:
+
+.. code-block:: jinja
+
+    {% for i in [1, 2, 3, 4, 5]|slice(1, 2) %}
+        {# will iterate over 2 and 3 #}
+    {% endfor %}
+
+    {{ '12345'|slice(1, 2) }}
+
+    {# outputs 23 #}
+
+You can use any valid expression for both the start and the length:
+
+.. code-block:: jinja
+
+    {% for i in [1, 2, 3, 4, 5]|slice(start, length) %}
+        {# ... #}
+    {% endfor %}
+
+As syntactic sugar, you can also use the ``[]`` notation:
+
+.. code-block:: jinja
+
+    {% for i in [1, 2, 3, 4, 5][start:length] %}
+        {# ... #}
+    {% endfor %}
+
+    {{ '12345'[1:2] }}
+
+    {# you can omit the first argument -- which is the same as 0 #}
+    {{ '12345'[:2] }} {# will display "12" #}
+
+    {# you can omit the last argument -- which will select everything till the end #}
+    {{ '12345'[2:] }} {# will display "345" #}
+
+The ``slice`` filter works as the `array_slice`_ PHP function for arrays and
+`substr`_ for strings.
+
+If the start is non-negative, the sequence will start at that start in the
+variable. If start is negative, the sequence will start that far from the end
+of the variable.
+
+If length is given and is positive, then the sequence will have up to that
+many elements in it. If the variable is shorter than the length, then only the
+available variable elements will be present. If length is given and is
+negative then the sequence will stop that many elements from the end of the
+variable. If it is omitted, then the sequence will have everything from offset
+up until the end of the variable.
+
+.. note::
+
+    It also works with objects implementing the `Traversable`_ interface.
+
+Arguments
+---------
+
+ * ``start``:         The start of the slice
+ * ``length``:        The size of the slice
+ * ``preserve_keys``: Whether to preserve key or not (when the input is an array)
+
+.. _`Traversable`: http://php.net/manual/en/class.traversable.php
+.. _`array_slice`: http://php.net/array_slice
+.. _`substr`:      http://php.net/substr
diff --git a/vendor/twig/twig/doc/filters/sort.rst b/vendor/twig/twig/doc/filters/sort.rst
new file mode 100644 (file)
index 0000000..3331152
--- /dev/null
@@ -0,0 +1,17 @@
+``sort``
+========
+
+The ``sort`` filter sorts an array:
+
+.. code-block:: jinja
+
+    {% for user in users|sort %}
+        ...
+    {% endfor %}
+
+.. note::
+
+    Internally, Twig uses the PHP `asort`_ function to maintain index
+    association.
+
+.. _`asort`: http://php.net/asort
diff --git a/vendor/twig/twig/doc/filters/split.rst b/vendor/twig/twig/doc/filters/split.rst
new file mode 100644 (file)
index 0000000..7cd2ca5
--- /dev/null
@@ -0,0 +1,53 @@
+``split``
+=========
+
+.. versionadded:: 1.10.3
+    The split filter was added in Twig 1.10.3.
+
+The ``split`` filter splits a string by the given delimiter and returns a list
+of strings:
+
+.. code-block:: jinja
+
+    {{ "one,two,three"|split(',') }}
+    {# returns ['one', 'two', 'three'] #}
+
+You can also pass a ``limit`` argument:
+
+ * If ``limit`` is positive, the returned array will contain a maximum of
+   limit elements with the last element containing the rest of string;
+
+ * If ``limit`` is negative, all components except the last -limit are
+   returned;
+
+ * If ``limit`` is zero, then this is treated as 1.
+
+.. code-block:: jinja
+
+    {{ "one,two,three,four,five"|split(',', 3) }}
+    {# returns ['one', 'two', 'three,four,five'] #}
+
+If the ``delimiter`` is an empty string, then value will be split by equal
+chunks. Length is set by the ``limit`` argument (one character by default).
+
+.. code-block:: jinja
+
+    {{ "123"|split('') }}
+    {# returns ['1', '2', '3'] #}
+
+    {{ "aabbcc"|split('', 2) }}
+    {# returns ['aa', 'bb', 'cc'] #}
+
+.. note::
+
+    Internally, Twig uses the PHP `explode`_ or `str_split`_ (if delimiter is
+    empty) functions for string splitting.
+
+Arguments
+---------
+
+ * ``delimiter``: The delimiter
+ * ``limit``:     The limit argument
+
+.. _`explode`:   http://php.net/explode
+.. _`str_split`: http://php.net/str_split
diff --git a/vendor/twig/twig/doc/filters/striptags.rst b/vendor/twig/twig/doc/filters/striptags.rst
new file mode 100644 (file)
index 0000000..72c6f25
--- /dev/null
@@ -0,0 +1,15 @@
+``striptags``
+=============
+
+The ``striptags`` filter strips SGML/XML tags and replace adjacent whitespace
+by one space:
+
+.. code-block:: jinja
+
+    {{ some_html|striptags }}
+
+.. note::
+
+    Internally, Twig uses the PHP `strip_tags`_ function.
+
+.. _`strip_tags`: http://php.net/strip_tags
diff --git a/vendor/twig/twig/doc/filters/title.rst b/vendor/twig/twig/doc/filters/title.rst
new file mode 100644 (file)
index 0000000..c5a318e
--- /dev/null
@@ -0,0 +1,11 @@
+``title``
+=========
+
+The ``title`` filter returns a titlecased version of the value. Words will
+start with uppercase letters, all remaining characters are lowercase:
+
+.. code-block:: jinja
+
+    {{ 'my first car'|title }}
+
+    {# outputs 'My First Car' #}
diff --git a/vendor/twig/twig/doc/filters/trim.rst b/vendor/twig/twig/doc/filters/trim.rst
new file mode 100644 (file)
index 0000000..f38afd5
--- /dev/null
@@ -0,0 +1,29 @@
+``trim``
+========
+
+.. versionadded:: 1.6.2
+    The trim filter was added in Twig 1.6.2.
+
+The ``trim`` filter strips whitespace (or other characters) from the beginning
+and end of a string:
+
+.. code-block:: jinja
+
+    {{ '  I like Twig.  '|trim }}
+
+    {# outputs 'I like Twig.' #}
+
+    {{ '  I like Twig.'|trim('.') }}
+
+    {# outputs '  I like Twig' #}
+
+.. note::
+
+    Internally, Twig uses the PHP `trim`_ function.
+
+Arguments
+---------
+
+ * ``character_mask``: The characters to strip
+
+.. _`trim`: http://php.net/trim
diff --git a/vendor/twig/twig/doc/filters/upper.rst b/vendor/twig/twig/doc/filters/upper.rst
new file mode 100644 (file)
index 0000000..561cebe
--- /dev/null
@@ -0,0 +1,10 @@
+``upper``
+=========
+
+The ``upper`` filter converts a value to uppercase:
+
+.. code-block:: jinja
+
+    {{ 'welcome'|upper }}
+
+    {# outputs 'WELCOME' #}
diff --git a/vendor/twig/twig/doc/filters/url_encode.rst b/vendor/twig/twig/doc/filters/url_encode.rst
new file mode 100644 (file)
index 0000000..b4f9a6c
--- /dev/null
@@ -0,0 +1,28 @@
+``url_encode``
+==============
+
+.. versionadded:: 1.12.3
+    Support for encoding an array as query string was added in Twig 1.12.3.
+
+The ``url_encode`` filter percent encodes a given string as URL segment
+or an array as query string:
+
+.. code-block:: jinja
+
+    {{ "path-seg*ment"|url_encode }}
+    {# outputs "path-seg%2Ament" #}
+
+    {{ "string with spaces"|url_encode(true) }}
+    {# outputs "string%20with%20spaces" #}
+    
+    {{ {'param': 'value', 'foo': 'bar'}|url_encode }}
+    {# outputs "param=value&foo=bar" #}
+
+.. note::
+
+    Internally, Twig uses the PHP `urlencode`_ (or `rawurlencode`_ if you pass
+    ``true`` as the first parameter) or the `http_build_query`_ function.
+
+.. _`urlencode`:        http://php.net/urlencode
+.. _`rawurlencode`:     http://php.net/rawurlencode
+.. _`http_build_query`: http://php.net/http_build_query
diff --git a/vendor/twig/twig/doc/functions/attribute.rst b/vendor/twig/twig/doc/functions/attribute.rst
new file mode 100644 (file)
index 0000000..3051bda
--- /dev/null
@@ -0,0 +1,18 @@
+``attribute``
+=============
+
+.. versionadded:: 1.2
+    The ``attribute`` function was added in Twig 1.2.
+
+``attribute`` can be used to access a "dynamic" attribute of a variable:
+
+.. code-block:: jinja
+
+    {{ attribute(object, method) }}
+    {{ attribute(object, method, arguments) }}
+    {{ attribute(array, item) }}
+
+.. note::
+
+    The resolution algorithm is the same as the one used for the ``.``
+    notation, except that the item can be any valid expression.
diff --git a/vendor/twig/twig/doc/functions/block.rst b/vendor/twig/twig/doc/functions/block.rst
new file mode 100644 (file)
index 0000000..fd571ef
--- /dev/null
@@ -0,0 +1,15 @@
+``block``
+=========
+
+When a template uses inheritance and if you want to print a block multiple
+times, use the ``block`` function:
+
+.. code-block:: jinja
+
+    <title>{% block title %}{% endblock %}</title>
+
+    <h1>{{ block('title') }}</h1>
+
+    {% block body %}{% endblock %}
+
+.. seealso:: :doc:`extends<../tags/extends>`, :doc:`parent<../functions/parent>`
diff --git a/vendor/twig/twig/doc/functions/constant.rst b/vendor/twig/twig/doc/functions/constant.rst
new file mode 100644 (file)
index 0000000..bea0e9f
--- /dev/null
@@ -0,0 +1,18 @@
+``constant``
+============
+
+.. versionadded: 1.12.1
+    constant now accepts object instances as the second argument.
+
+``constant`` returns the constant value for a given string:
+
+.. code-block:: jinja
+
+    {{ some_date|date(constant('DATE_W3C')) }}
+    {{ constant('Namespace\\Classname::CONSTANT_NAME') }}
+
+As of 1.12.1 you can read constants from object instances as well:
+
+.. code-block:: jinja
+
+    {{ constant('RSS', date) }}
diff --git a/vendor/twig/twig/doc/functions/cycle.rst b/vendor/twig/twig/doc/functions/cycle.rst
new file mode 100644 (file)
index 0000000..0015cae
--- /dev/null
@@ -0,0 +1,25 @@
+``cycle``
+=========
+
+The ``cycle`` function cycles on an array of values:
+
+.. code-block:: jinja
+
+    {% for i in 0..10 %}
+        {{ cycle(['odd', 'even'], i) }}
+    {% endfor %}
+
+The array can contain any number of values:
+
+.. code-block:: jinja
+
+    {% set fruits = ['apple', 'orange', 'citrus'] %}
+
+    {% for i in 0..10 %}
+        {{ cycle(fruits, i) }}
+    {% endfor %}
+
+Arguments
+---------
+
+ * ``position``: The cycle position
diff --git a/vendor/twig/twig/doc/functions/date.rst b/vendor/twig/twig/doc/functions/date.rst
new file mode 100644 (file)
index 0000000..f1c9481
--- /dev/null
@@ -0,0 +1,52 @@
+``date``
+========
+
+.. versionadded:: 1.6
+    The date function has been added in Twig 1.6.
+
+.. versionadded:: 1.6.1
+    The default timezone support has been added in Twig 1.6.1.
+
+Converts an argument to a date to allow date comparison:
+
+.. code-block:: jinja
+
+    {% if date(user.created_at) < date('-2days') %}
+        {# do something #}
+    {% endif %}
+
+The argument must be in a format supported by the `date`_ function.
+
+You can pass a timezone as the second argument:
+
+.. code-block:: jinja
+
+    {% if date(user.created_at) < date('-2days', 'Europe/Paris') %}
+        {# do something #}
+    {% endif %}
+
+If no argument is passed, the function returns the current date:
+
+.. code-block:: jinja
+
+    {% if date(user.created_at) < date() %}
+        {# always! #}
+    {% endif %}
+
+.. note::
+
+    You can set the default timezone globally by calling ``setTimezone()`` on
+    the ``core`` extension instance:
+
+    .. code-block:: php
+
+        $twig = new Twig_Environment($loader);
+        $twig->getExtension('core')->setTimezone('Europe/Paris');
+
+Arguments
+---------
+
+ * ``date``:     The date
+ * ``timezone``: The timezone
+
+.. _`date`: http://www.php.net/date
diff --git a/vendor/twig/twig/doc/functions/dump.rst b/vendor/twig/twig/doc/functions/dump.rst
new file mode 100644 (file)
index 0000000..1500b0f
--- /dev/null
@@ -0,0 +1,69 @@
+``dump``
+========
+
+.. versionadded:: 1.5
+    The dump function was added in Twig 1.5.
+
+The ``dump`` function dumps information about a template variable. This is
+mostly useful to debug a template that does not behave as expected by
+introspecting its variables:
+
+.. code-block:: jinja
+
+    {{ dump(user) }}
+
+.. note::
+
+    The ``dump`` function is not available by default. You must add the
+    ``Twig_Extension_Debug`` extension explicitly when creating your Twig
+    environment::
+
+        $twig = new Twig_Environment($loader, array(
+            'debug' => true,
+            // ...
+        ));
+        $twig->addExtension(new Twig_Extension_Debug());
+
+    Even when enabled, the ``dump`` function won't display anything if the
+    ``debug`` option on the environment is not enabled (to avoid leaking debug
+    information on a production server).
+
+In an HTML context, wrap the output with a ``pre`` tag to make it easier to
+read:
+
+.. code-block:: jinja
+
+    <pre>
+        {{ dump(user) }}
+    </pre>
+
+.. tip::
+
+    Using a ``pre`` tag is not needed when `XDebug`_ is enabled and
+    ``html_errors`` is ``on``; as a bonus, the output is also nicer with
+    XDebug enabled.
+
+You can debug several variables by passing them as additional arguments:
+
+.. code-block:: jinja
+
+    {{ dump(user, categories) }}
+
+If you don't pass any value, all variables from the current context are
+dumped:
+
+.. code-block:: jinja
+
+    {{ dump() }}
+
+.. note::
+
+    Internally, Twig uses the PHP `var_dump`_ function.
+
+Arguments
+---------
+
+ * ``context``: The context to dump
+
+.. _`XDebug`:   http://xdebug.org/docs/display
+.. _`var_dump`: http://php.net/var_dump
diff --git a/vendor/twig/twig/doc/functions/include.rst b/vendor/twig/twig/doc/functions/include.rst
new file mode 100644 (file)
index 0000000..eaddfe6
--- /dev/null
@@ -0,0 +1,80 @@
+``include``
+===========
+
+.. versionadded:: 1.12
+    The include function was added in Twig 1.12.
+
+The ``include`` function returns the rendered content of a template:
+
+.. code-block:: jinja
+
+    {{ include('template.html') }}
+    {{ include(some_var) }}
+
+Included templates have access to the variables of the active context.
+
+If you are using the filesystem loader, the templates are looked for in the
+paths defined by it.
+
+The context is passed by default to the template but you can also pass
+additional variables:
+
+.. code-block:: jinja
+
+    {# template.html will have access to the variables from the current context and the additional ones provided #}
+    {{ include('template.html', {foo: 'bar'}) }}
+
+You can disable access to the context by setting ``with_context`` to
+``false``:
+
+.. code-block:: jinja
+
+    {# only the foo variable will be accessible #}
+    {{ include('template.html', {foo: 'bar'}, with_context = false) }}
+
+.. code-block:: jinja
+
+    {# no variables will be accessible #}
+    {{ include('template.html', with_context = false) }}
+
+And if the expression evaluates to a ``Twig_Template`` object, Twig will use it
+directly::
+
+    // {{ include(template) }}
+
+    $template = $twig->loadTemplate('some_template.twig');
+
+    $twig->loadTemplate('template.twig')->display(array('template' => $template));
+
+When you set the ``ignore_missing`` flag, Twig will return an empty string if
+the template does not exist:
+
+.. code-block:: jinja
+
+    {{ include('sidebar.html', ignore_missing = true) }}
+
+You can also provide a list of templates that are checked for existence before
+inclusion. The first template that exists will be rendered:
+
+.. code-block:: jinja
+
+    {{ include(['page_detailed.html', 'page.html']) }}
+
+If ``ignore_missing`` is set, it will fall back to rendering nothing if none
+of the templates exist, otherwise it will throw an exception.
+
+When including a template created by an end user, you should consider
+sandboxing it:
+
+.. code-block:: jinja
+
+    {{ include('page.html', sandboxed = true) }}
+
+Arguments
+---------
+
+ * ``template``:       The template to render
+ * ``variables``:      The variables to pass to the template
+ * ``with_context``:   Whether to pass the current context variables or not
+ * ``ignore_missing``: Whether to ignore missing templates or not
+ * ``sandboxed``:      Whether to sandbox the template or not
diff --git a/vendor/twig/twig/doc/functions/index.rst b/vendor/twig/twig/doc/functions/index.rst
new file mode 100644 (file)
index 0000000..8650cbd
--- /dev/null
@@ -0,0 +1,17 @@
+Functions
+=========
+
+.. toctree::
+    :maxdepth: 1
+
+    attribute
+    block
+    constant
+    cycle
+    date
+    dump
+    include
+    parent
+    random
+    range
+    template_from_string
diff --git a/vendor/twig/twig/doc/functions/parent.rst b/vendor/twig/twig/doc/functions/parent.rst
new file mode 100644 (file)
index 0000000..f5bd200
--- /dev/null
@@ -0,0 +1,20 @@
+``parent``
+==========
+
+When a template uses inheritance, it's possible to render the contents of the
+parent block when overriding a block by using the ``parent`` function:
+
+.. code-block:: jinja
+
+    {% extends "base.html" %}
+
+    {% block sidebar %}
+        <h3>Table Of Contents</h3>
+        ...
+        {{ parent() }}
+    {% endblock %}
+
+The ``parent()`` call will return the content of the ``sidebar`` block as
+defined in the ``base.html`` template.
+
+.. seealso:: :doc:`extends<../tags/extends>`, :doc:`block<../functions/block>`, :doc:`block<../tags/block>`
diff --git a/vendor/twig/twig/doc/functions/random.rst b/vendor/twig/twig/doc/functions/random.rst
new file mode 100644 (file)
index 0000000..a5a916b
--- /dev/null
@@ -0,0 +1,29 @@
+``random``
+==========
+
+.. versionadded:: 1.5
+    The random function was added in Twig 1.5.
+
+.. versionadded:: 1.6
+    String and integer handling was added in Twig 1.6.
+
+The ``random`` function returns a random value depending on the supplied
+parameter type:
+
+* a random item from a sequence;
+* a random character from a string;
+* a random integer between 0 and the integer parameter (inclusive).
+
+.. code-block:: jinja
+
+    {{ random(['apple', 'orange', 'citrus']) }} {# example output: orange #}
+    {{ random('ABC') }}                         {# example output: C #}
+    {{ random() }}                              {# example output: 15386094 (works as native PHP `mt_rand`_ function) #}
+    {{ random(5) }}                             {# example output: 3 #}
+
+Arguments
+---------
+
+ * ``values``: The values
+
+.. _`mt_rand`: http://php.net/mt_rand
diff --git a/vendor/twig/twig/doc/functions/range.rst b/vendor/twig/twig/doc/functions/range.rst
new file mode 100644 (file)
index 0000000..b1fa547
--- /dev/null
@@ -0,0 +1,45 @@
+``range``
+=========
+
+Returns a list containing an arithmetic progression of integers:
+
+.. code-block:: jinja
+
+    {% for i in range(0, 3) %}
+        {{ i }},
+    {% endfor %}
+
+    {# returns 0, 1, 2, 3 #}
+
+When step is given (as the third parameter), it specifies the increment (or
+decrement):
+
+.. code-block:: jinja
+
+    {% for i in range(0, 6, 2) %}
+        {{ i }},
+    {% endfor %}
+
+    {# returns 0, 2, 4, 6 #}
+
+The Twig built-in ``..`` operator is just syntactic sugar for the ``range``
+function (with a step of 1):
+
+.. code-block:: jinja
+
+    {% for i in 0..3 %}
+        {{ i }},
+    {% endfor %}
+
+.. tip::
+
+    The ``range`` function works as the native PHP `range`_ function.
+
+Arguments
+---------
+
+ * ``low``:  The first value of the sequence.
+ * ``high``: The highest possible value of the sequence.
+ * ``step``: The increment between elements of the sequence.
+
+.. _`range`: http://php.net/range
diff --git a/vendor/twig/twig/doc/functions/template_from_string.rst b/vendor/twig/twig/doc/functions/template_from_string.rst
new file mode 100644 (file)
index 0000000..bbb06d8
--- /dev/null
@@ -0,0 +1,32 @@
+``template_from_string``
+========================
+
+.. versionadded:: 1.11
+    The template_from_string function was added in Twig 1.11.
+
+The ``template_from_string`` function loads a template from a string:
+
+.. code-block:: jinja
+
+    {{ include(template_from_string("Hello {{ name }}") }}
+    {{ include(template_from_string(page.template)) }}
+
+.. note::
+
+    The ``template_from_string`` function is not available by default. You
+    must add the ``Twig_Extension_StringLoader`` extension explicitly when
+    creating your Twig environment::
+
+        $twig = new Twig_Environment(...);
+        $twig->addExtension(new Twig_Extension_StringLoader());
+
+.. note::
+
+    Even if you will probably always use the ``template_from_string`` function
+    with the ``include`` function, you can use it with any tag or function that
+    takes a template as an argument (like the ``embed`` or ``extends`` tags).
+
+Arguments
+---------
+
+ * ``template``: The template
diff --git a/vendor/twig/twig/doc/index.rst b/vendor/twig/twig/doc/index.rst
new file mode 100644 (file)
index 0000000..3e5166c
--- /dev/null
@@ -0,0 +1,18 @@
+Twig
+====
+
+.. toctree::
+    :maxdepth: 2
+
+    intro
+    templates
+    api
+    advanced
+    internals
+    recipes
+    coding_standards
+    tags/index
+    filters/index
+    functions/index
+    tests/index
+    deprecated
diff --git a/vendor/twig/twig/doc/internals.rst b/vendor/twig/twig/doc/internals.rst
new file mode 100644 (file)
index 0000000..79a3c8d
--- /dev/null
@@ -0,0 +1,140 @@
+Twig Internals
+==============
+
+Twig is very extensible and you can easily hack it. Keep in mind that you
+should probably try to create an extension before hacking the core, as most
+features and enhancements can be done with extensions. This chapter is also
+useful for people who want to understand how Twig works under the hood.
+
+How Twig works?
+---------------
+
+The rendering of a Twig template can be summarized into four key steps:
+
+* **Load** the template: If the template is already compiled, load it and go
+  to the *evaluation* step, otherwise:
+
+  * First, the **lexer** tokenizes the template source code into small pieces
+    for easier processing;
+  * Then, the **parser** converts the token stream into a meaningful tree
+    of nodes (the Abstract Syntax Tree);
+  * Eventually, the *compiler* transforms the AST into PHP code;
+
+* **Evaluate** the template: It basically means calling the ``display()``
+  method of the compiled template and passing it the context.
+
+The Lexer
+---------
+
+The lexer tokenizes a template source code into a token stream (each token is
+an instance of ``Twig_Token``, and the stream is an instance of
+``Twig_TokenStream``). The default lexer recognizes 13 different token types:
+
+* ``Twig_Token::BLOCK_START_TYPE``, ``Twig_Token::BLOCK_END_TYPE``: Delimiters for blocks (``{% %}``)
+* ``Twig_Token::VAR_START_TYPE``, ``Twig_Token::VAR_END_TYPE``: Delimiters for variables (``{{ }}``)
+* ``Twig_Token::TEXT_TYPE``: A text outside an expression;
+* ``Twig_Token::NAME_TYPE``: A name in an expression;
+* ``Twig_Token::NUMBER_TYPE``: A number in an expression;
+* ``Twig_Token::STRING_TYPE``: A string in an expression;
+* ``Twig_Token::OPERATOR_TYPE``: An operator;
+* ``Twig_Token::PUNCTUATION_TYPE``: A punctuation sign;
+* ``Twig_Token::INTERPOLATION_START_TYPE``, ``Twig_Token::INTERPOLATION_END_TYPE`` (as of Twig 1.5): Delimiters for string interpolation;
+* ``Twig_Token::EOF_TYPE``: Ends of template.
+
+You can manually convert a source code into a token stream by calling the
+``tokenize()`` of an environment::
+
+    $stream = $twig->tokenize($source, $identifier);
+
+As the stream has a ``__toString()`` method, you can have a textual
+representation of it by echoing the object::
+
+    echo $stream."\n";
+
+Here is the output for the ``Hello {{ name }}`` template:
+
+.. code-block:: text
+
+    TEXT_TYPE(Hello )
+    VAR_START_TYPE()
+    NAME_TYPE(name)
+    VAR_END_TYPE()
+    EOF_TYPE()
+
+.. note::
+
+    You can change the default lexer use by Twig (``Twig_Lexer``) by calling
+    the ``setLexer()`` method::
+
+        $twig->setLexer($lexer);
+
+The Parser
+----------
+
+The parser converts the token stream into an AST (Abstract Syntax Tree), or a
+node tree (an instance of ``Twig_Node_Module``). The core extension defines
+the basic nodes like: ``for``, ``if``, ... and the expression nodes.
+
+You can manually convert a token stream into a node tree by calling the
+``parse()`` method of an environment::
+
+    $nodes = $twig->parse($stream);
+
+Echoing the node object gives you a nice representation of the tree::
+
+    echo $nodes."\n";
+
+Here is the output for the ``Hello {{ name }}`` template:
+
+.. code-block:: text
+
+    Twig_Node_Module(
+      Twig_Node_Text(Hello )
+      Twig_Node_Print(
+        Twig_Node_Expression_Name(name)
+      )
+    )
+
+.. note::
+
+    The default parser (``Twig_TokenParser``) can be also changed by calling the
+    ``setParser()`` method::
+
+        $twig->setParser($parser);
+
+The Compiler
+------------
+
+The last step is done by the compiler. It takes a node tree as an input and
+generates PHP code usable for runtime execution of the template.
+
+You can call the compiler by hand with the ``compile()`` method of an
+environment::
+
+    $php = $twig->compile($nodes);
+
+The ``compile()`` method returns the PHP source code representing the node.
+
+The generated template for a ``Hello {{ name }}`` template reads as follows
+(the actual output can differ depending on the version of Twig you are
+using)::
+
+    /* Hello {{ name }} */
+    class __TwigTemplate_1121b6f109fe93ebe8c6e22e3712bceb extends Twig_Template
+    {
+        protected function doDisplay(array $context, array $blocks = array())
+        {
+            // line 1
+            echo "Hello ";
+            echo twig_escape_filter($this->env, $this->getContext($context, "name"), "ndex", null, true);
+        }
+
+        // some more code
+    }
+
+.. note::
+
+    As for the lexer and the parser, the default compiler (``Twig_Compiler``) can
+    be changed by calling the ``setCompiler()`` method::
+
+        $twig->setCompiler($compiler);
diff --git a/vendor/twig/twig/doc/intro.rst b/vendor/twig/twig/doc/intro.rst
new file mode 100644 (file)
index 0000000..bdcdb8a
--- /dev/null
@@ -0,0 +1,164 @@
+Introduction
+============
+
+This is the documentation for Twig, the flexible, fast, and secure template
+engine for PHP.
+
+If you have any exposure to other text-based template languages, such as
+Smarty, Django, or Jinja, you should feel right at home with Twig. It's both
+designer and developer friendly by sticking to PHP's principles and adding
+functionality useful for templating environments.
+
+The key-features are...
+
+* *Fast*: Twig compiles templates down to plain optimized PHP code. The
+  overhead compared to regular PHP code was reduced to the very minimum.
+
+* *Secure*: Twig has a sandbox mode to evaluate untrusted template code. This
+  allows Twig to be used as a template language for applications where users
+  may modify the template design.
+
+* *Flexible*: Twig is powered by a flexible lexer and parser. This allows the
+  developer to define its own custom tags and filters, and create its own DSL.
+
+Prerequisites
+-------------
+
+Twig needs at least **PHP 5.2.4** to run.
+
+Installation
+------------
+
+You have multiple ways to install Twig.
+
+Installing via Composer (recommended)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+1. Install composer in your project:
+
+.. code-block:: bash
+
+    curl -s http://getcomposer.org/installer | php
+
+2. Create a ``composer.json`` file in your project root:
+
+.. code-block:: javascript
+
+    {
+        "require": {
+            "twig/twig": "1.*"
+        }
+    }
+
+3. Install via composer
+
+.. code-block:: bash
+
+    php composer.phar install
+
+.. note::
+    If you want to learn more about Composer, the ``composer.json`` file syntax
+    and its usage, you can read the `online documentation`_.
+
+Installing from the tarball release
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+1. Download the most recent tarball from the `download page`_
+2. Unpack the tarball
+3. Move the files somewhere in your project
+
+Installing the development version
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+1. Install Git
+2. ``git clone git://github.com/fabpot/Twig.git``
+
+Installing the PEAR package
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+1. Install PEAR
+2. ``pear channel-discover pear.twig-project.org``
+3. ``pear install twig/Twig`` (or ``pear install twig/Twig-beta``)
+
+
+Installing the C extension
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. versionadded:: 1.4
+    The C extension was added in Twig 1.4.
+
+Twig comes with a C extension that enhances the performance of the Twig
+runtime engine. You can install it like any other PHP extension:
+
+.. code-block:: bash
+
+    $ cd ext/twig
+    $ phpize
+    $ ./configure
+    $ make
+    $ make install
+
+Finally, enable the extension in your ``php.ini`` configuration file:
+
+.. code-block:: ini
+
+    extension=twig.so
+
+And from now on, Twig will automatically compile your templates to take
+advantage of the C extension. Note that this extension does not replace the
+PHP code but only provides an optimized version of the
+``Twig_Template::getAttribute()`` method.
+
+.. tip::
+
+    On Windows, you can also simply download and install a `pre-built DLL`_.
+
+Basic API Usage
+---------------
+
+This section gives you a brief introduction to the PHP API for Twig.
+
+The first step to use Twig is to register its autoloader::
+
+    require_once '/path/to/lib/Twig/Autoloader.php';
+    Twig_Autoloader::register();
+
+Replace the ``/path/to/lib/`` path with the path you used for Twig
+installation.
+
+If you have installed Twig via Composer you can take advantage of Composer's
+autoload mechanism by replacing the previous snippet for::
+
+    require_once '/path/to/vendor/autoload.php';
+
+.. note::
+
+    Twig follows the PEAR convention names for its classes, which means you
+    can easily integrate Twig classes loading in your own autoloader.
+
+.. code-block:: php
+
+    $loader = new Twig_Loader_String();
+    $twig = new Twig_Environment($loader);
+
+    echo $twig->render('Hello {{ name }}!', array('name' => 'Fabien'));
+
+Twig uses a loader (``Twig_Loader_String``) to locate templates, and an
+environment (``Twig_Environment``) to store the configuration.
+
+The ``render()`` method loads the template passed as a first argument and
+renders it with the variables passed as a second argument.
+
+As templates are generally stored on the filesystem, Twig also comes with a
+filesystem loader::
+
+    $loader = new Twig_Loader_Filesystem('/path/to/templates');
+    $twig = new Twig_Environment($loader, array(
+        'cache' => '/path/to/compilation_cache',
+    ));
+
+    echo $twig->render('index.html', array('name' => 'Fabien'));
+
+.. _`download page`: https://github.com/fabpot/Twig/tags
+.. _`online documentation`: http://getcomposer.org/doc
+.. _`pre-built DLL`: https://github.com/stealth35/stealth35.github.com/downloads
diff --git a/vendor/twig/twig/doc/recipes.rst b/vendor/twig/twig/doc/recipes.rst
new file mode 100644 (file)
index 0000000..dfcc920
--- /dev/null
@@ -0,0 +1,475 @@
+Recipes
+=======
+
+Making a Layout conditional
+---------------------------
+
+Working with Ajax means that the same content is sometimes displayed as is,
+and sometimes decorated with a layout. As Twig layout template names can be
+any valid expression, you can pass a variable that evaluates to ``true`` when
+the request is made via Ajax and choose the layout accordingly:
+
+.. code-block:: jinja
+
+    {% extends request.ajax ? "base_ajax.html" : "base.html" %}
+
+    {% block content %}
+        This is the content to be displayed.
+    {% endblock %}
+
+Making an Include dynamic
+-------------------------
+
+When including a template, its name does not need to be a string. For
+instance, the name can depend on the value of a variable:
+
+.. code-block:: jinja
+
+    {% include var ~ '_foo.html' %}
+
+If ``var`` evaluates to ``index``, the ``index_foo.html`` template will be
+rendered.
+
+As a matter of fact, the template name can be any valid expression, such as
+the following:
+
+.. code-block:: jinja
+
+    {% include var|default('index') ~ '_foo.html' %}
+
+Overriding a Template that also extends itself
+----------------------------------------------
+
+A template can be customized in two different ways:
+
+* *Inheritance*: A template *extends* a parent template and overrides some
+  blocks;
+
+* *Replacement*: If you use the filesystem loader, Twig loads the first
+  template it finds in a list of configured directories; a template found in a
+  directory *replaces* another one from a directory further in the list.
+
+But how do you combine both: *replace* a template that also extends itself
+(aka a template in a directory further in the list)?
+
+Let's say that your templates are loaded from both ``.../templates/mysite``
+and ``.../templates/default`` in this order. The ``page.twig`` template,
+stored in ``.../templates/default`` reads as follows:
+
+.. code-block:: jinja
+
+    {# page.twig #}
+    {% extends "layout.twig" %}
+
+    {% block content %}
+    {% endblock %}
+
+You can replace this template by putting a file with the same name in
+``.../templates/mysite``. And if you want to extend the original template, you
+might be tempted to write the following:
+
+.. code-block:: jinja
+
+    {# page.twig in .../templates/mysite #}
+    {% extends "page.twig" %} {# from .../templates/default #}
+
+Of course, this will not work as Twig will always load the template from
+``.../templates/mysite``.
+
+It turns out it is possible to get this to work, by adding a directory right
+at the end of your template directories, which is the parent of all of the
+other directories: ``.../templates`` in our case. This has the effect of
+making every template file within our system uniquely addressable. Most of the
+time you will use the "normal" paths, but in the special case of wanting to
+extend a template with an overriding version of itself we can reference its
+parent's full, unambiguous template path in the extends tag:
+
+.. code-block:: jinja
+
+    {# page.twig in .../templates/mysite #}
+    {% extends "default/page.twig" %} {# from .../templates #}
+
+.. note::
+
+    This recipe was inspired by the following Django wiki page:
+    http://code.djangoproject.com/wiki/ExtendingTemplates
+
+Customizing the Syntax
+----------------------
+
+Twig allows some syntax customization for the block delimiters. It's not
+recommended to use this feature as templates will be tied with your custom
+syntax. But for specific projects, it can make sense to change the defaults.
+
+To change the block delimiters, you need to create your own lexer object::
+
+    $twig = new Twig_Environment();
+
+    $lexer = new Twig_Lexer($twig, array(
+        'tag_comment'   => array('{#', '#}'),
+        'tag_block'     => array('{%', '%}'),
+        'tag_variable'  => array('{{', '}}'),
+        'interpolation' => array('#{', '}'),
+    ));
+    $twig->setLexer($lexer);
+
+Here are some configuration example that simulates some other template engines
+syntax::
+
+    // Ruby erb syntax
+    $lexer = new Twig_Lexer($twig, array(
+        'tag_comment'  => array('<%#', '%>'),
+        'tag_block'    => array('<%', '%>'),
+        'tag_variable' => array('<%=', '%>'),
+    ));
+
+    // SGML Comment Syntax
+    $lexer = new Twig_Lexer($twig, array(
+        'tag_comment'  => array('<!--#', '-->'),
+        'tag_block'    => array('<!--', '-->'),
+        'tag_variable' => array('${', '}'),
+    ));
+
+    // Smarty like
+    $lexer = new Twig_Lexer($twig, array(
+        'tag_comment'  => array('{*', '*}'),
+        'tag_block'    => array('{', '}'),
+        'tag_variable' => array('{$', '}'),
+    ));
+
+Using dynamic Object Properties
+-------------------------------
+
+When Twig encounters a variable like ``article.title``, it tries to find a
+``title`` public property in the ``article`` object.
+
+It also works if the property does not exist but is rather defined dynamically
+thanks to the magic ``__get()`` method; you just need to also implement the
+``__isset()`` magic method like shown in the following snippet of code::
+
+    class Article
+    {
+        public function __get($name)
+        {
+            if ('title' == $name) {
+                return 'The title';
+            }
+
+            // throw some kind of error
+        }
+
+        public function __isset($name)
+        {
+            if ('title' == $name) {
+                return true;
+            }
+
+            return false;
+        }
+    }
+
+Accessing the parent Context in Nested Loops
+--------------------------------------------
+
+Sometimes, when using nested loops, you need to access the parent context. The
+parent context is always accessible via the ``loop.parent`` variable. For
+instance, if you have the following template data::
+
+    $data = array(
+        'topics' => array(
+            'topic1' => array('Message 1 of topic 1', 'Message 2 of topic 1'),
+            'topic2' => array('Message 1 of topic 2', 'Message 2 of topic 2'),
+        ),
+    );
+
+And the following template to display all messages in all topics:
+
+.. code-block:: jinja
+
+    {% for topic, messages in topics %}
+        * {{ loop.index }}: {{ topic }}
+      {% for message in messages %}
+          - {{ loop.parent.loop.index }}.{{ loop.index }}: {{ message }}
+      {% endfor %}
+    {% endfor %}
+
+The output will be similar to:
+
+.. code-block:: text
+
+    * 1: topic1
+      - 1.1: The message 1 of topic 1
+      - 1.2: The message 2 of topic 1
+    * 2: topic2
+      - 2.1: The message 1 of topic 2
+      - 2.2: The message 2 of topic 2
+
+In the inner loop, the ``loop.parent`` variable is used to access the outer
+context. So, the index of the current ``topic`` defined in the outer for loop
+is accessible via the ``loop.parent.loop.index`` variable.
+
+Defining undefined Functions and Filters on the Fly
+---------------------------------------------------
+
+When a function (or a filter) is not defined, Twig defaults to throw a
+``Twig_Error_Syntax`` exception. However, it can also call a `callback`_ (any
+valid PHP callable) which should return a function (or a filter).
+
+For filters, register callbacks with ``registerUndefinedFilterCallback()``.
+For functions, use ``registerUndefinedFunctionCallback()``::
+
+    // auto-register all native PHP functions as Twig functions
+    // don't try this at home as it's not secure at all!
+    $twig->registerUndefinedFunctionCallback(function ($name) {
+        if (function_exists($name)) {
+            return new Twig_Function_Function($name);
+        }
+
+        return false;
+    });
+
+If the callable is not able to return a valid function (or filter), it must
+return ``false``.
+
+If you register more than one callback, Twig will call them in turn until one
+does not return ``false``.
+
+.. tip::
+
+    As the resolution of functions and filters is done during compilation,
+    there is no overhead when registering these callbacks.
+
+Validating the Template Syntax
+------------------------------
+
+When template code is providing by a third-party (through a web interface for
+instance), it might be interesting to validate the template syntax before
+saving it. If the template code is stored in a `$template` variable, here is
+how you can do it::
+
+    try {
+        $twig->parse($twig->tokenize($template));
+
+        // the $template is valid
+    } catch (Twig_Error_Syntax $e) {
+        // $template contains one or more syntax errors
+    }
+
+If you iterate over a set of files, you can pass the filename to the
+``tokenize()`` method to get the filename in the exception message::
+
+    foreach ($files as $file) {
+        try {
+            $twig->parse($twig->tokenize($template, $file));
+
+            // the $template is valid
+        } catch (Twig_Error_Syntax $e) {
+            // $template contains one or more syntax errors
+        }
+    }
+
+.. note::
+
+    This method won't catch any sandbox policy violations because the policy
+    is enforced during template rendering (as Twig needs the context for some
+    checks like allowed methods on objects).
+
+Refreshing modified Templates when APC is enabled and apc.stat = 0
+------------------------------------------------------------------
+
+When using APC with ``apc.stat`` set to ``0`` and Twig cache enabled, clearing
+the template cache won't update the APC cache. To get around this, one can
+extend ``Twig_Environment`` and force the update of the APC cache when Twig
+rewrites the cache::
+
+    class Twig_Environment_APC extends Twig_Environment
+    {
+        protected function writeCacheFile($file, $content)
+        {
+            parent::writeCacheFile($file, $content);
+
+            // Compile cached file into bytecode cache
+            apc_compile_file($file);
+        }
+    }
+
+Reusing a stateful Node Visitor
+-------------------------------
+
+When attaching a visitor to a ``Twig_Environment`` instance, Twig uses it to
+visit *all* templates it compiles. If you need to keep some state information
+around, you probably want to reset it when visiting a new template.
+
+This can be easily achieved with the following code::
+
+    protected $someTemplateState = array();
+
+    public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
+    {
+        if ($node instanceof Twig_Node_Module) {
+            // reset the state as we are entering a new template
+            $this->someTemplateState = array();
+        }
+
+        // ...
+
+        return $node;
+    }
+
+Using the Template name to set the default Escaping Strategy
+------------------------------------------------------------
+
+.. versionadded:: 1.8
+    This recipe requires Twig 1.8 or later.
+
+The ``autoescape`` option determines the default escaping strategy to use when
+no escaping is applied on a variable. When Twig is used to mostly generate
+HTML files, you can set it to ``html`` and explicitly change it to ``js`` when
+you have some dynamic JavaScript files thanks to the ``autoescape`` tag:
+
+.. code-block:: jinja
+
+    {% autoescape 'js' %}
+        ... some JS ...
+    {% endautoescape %}
+
+But if you have many HTML and JS files, and if your template names follow some
+conventions, you can instead determine the default escaping strategy to use
+based on the template name. Let's say that your template names always ends
+with ``.html`` for HTML files, ``.js`` for JavaScript ones, and ``.css`` for
+stylesheets, here is how you can configure Twig::
+
+    class TwigEscapingGuesser
+    {
+        function guess($filename)
+        {
+            // get the format
+            $format = substr($filename, strrpos($filename, '.') + 1);
+
+            switch ($format) {
+                case 'js':
+                    return 'js';
+                case 'css':
+                    return 'css';
+                case 'html':
+                default:
+                    return 'html';
+            }
+        }
+    }
+
+    $loader = new Twig_Loader_Filesystem('/path/to/templates');
+    $twig = new Twig_Environment($loader, array(
+        'autoescape' => array(new TwigEscapingGuesser(), 'guess'),
+    ));
+
+This dynamic strategy does not incur any overhead at runtime as auto-escaping
+is done at compilation time.
+
+Using a Database to store Templates
+-----------------------------------
+
+If you are developing a CMS, templates are usually stored in a database. This
+recipe gives you a simple PDO template loader you can use as a starting point
+for your own.
+
+First, let's create a temporary in-memory SQLite3 database to work with::
+
+    $dbh = new PDO('sqlite::memory:');
+    $dbh->exec('CREATE TABLE templates (name STRING, source STRING, last_modified INTEGER)');
+    $base = '{% block content %}{% endblock %}';
+    $index = '
+    {% extends "base.twig" %}
+    {% block content %}Hello {{ name }}{% endblock %}
+    ';
+    $now = time();
+    $dbh->exec("INSERT INTO templates (name, source, last_modified) VALUES ('base.twig', '$base', $now)");
+    $dbh->exec("INSERT INTO templates (name, source, last_modified) VALUES ('index.twig', '$index', $now)");
+
+We have created a simple ``templates`` table that hosts two templates:
+``base.twig`` and ``index.twig``.
+
+Now, let's define a loader able to use this database::
+
+    class DatabaseTwigLoader implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
+    {
+        protected $dbh;
+
+        public function __construct(PDO $dbh)
+        {
+            $this->dbh = $dbh;
+        }
+
+        public function getSource($name)
+        {
+            if (false === $source = $this->getValue('source', $name)) {
+                throw new Twig_Error_Loader(sprintf('Template "%s" does not exist.', $name));
+            }
+
+            return $source;
+        }
+
+        // Twig_ExistsLoaderInterface as of Twig 1.11
+        public function exists($name)
+        {
+            return $name === $this->getValue('name', $name);
+        }
+
+        public function getCacheKey($name)
+        {
+            return $name;
+        }
+
+        public function isFresh($name, $time)
+        {
+            if (false === $lastModified = $this->getValue('last_modified', $name)) {
+                return false;
+            }
+
+            return $lastModified <= $time;
+        }
+
+        protected function getValue($column, $name)
+        {
+            $sth = $this->dbh->prepare('SELECT '.$column.' FROM templates WHERE name = :name');
+            $sth->execute(array(':name' => (string) $name));
+
+            return $sth->fetchColumn();
+        }
+    }
+
+Finally, here is an example on how you can use it::
+
+    $loader = new DatabaseTwigLoader($dbh);
+    $twig = new Twig_Environment($loader);
+
+    echo $twig->render('index.twig', array('name' => 'Fabien'));
+
+Using different Template Sources
+--------------------------------
+
+This recipe is the continuation of the previous one. Even if you store the
+contributed templates in a database, you might want to keep the original/base
+templates on the filesystem. When templates can be loaded from different
+sources, you need to use the ``Twig_Loader_Chain`` loader.
+
+As you can see in the previous recipe, we reference the template in the exact
+same way as we would have done it with a regular filesystem loader. This is
+the key to be able to mix and match templates coming from the database, the
+filesystem, or any other loader for that matter: the template name should be a
+logical name, and not the path from the filesystem::
+
+    $loader1 = new DatabaseTwigLoader($dbh);
+    $loader2 = new Twig_Loader_Array(array(
+        'base.twig' => '{% block content %}{% endblock %}',
+    ));
+    $loader = new Twig_Loader_Chain(array($loader1, $loader2));
+
+    $twig = new Twig_Environment($loader);
+
+    echo $twig->render('index.twig', array('name' => 'Fabien'));
+
+Now that the ``base.twig`` templates is defined in an array loader, you can
+remove it from the database, and everything else will still work as before.
+
+.. _callback: http://www.php.net/manual/en/function.is-callable.php
diff --git a/vendor/twig/twig/doc/tags/autoescape.rst b/vendor/twig/twig/doc/tags/autoescape.rst
new file mode 100644 (file)
index 0000000..c5ff0c2
--- /dev/null
@@ -0,0 +1,71 @@
+``autoescape``
+==============
+
+Whether automatic escaping is enabled or not, you can mark a section of a
+template to be escaped or not by using the ``autoescape`` tag:
+
+.. code-block:: jinja
+
+    {# The following syntax works as of Twig 1.8 -- see the note below for previous versions #}
+
+    {% autoescape %}
+        Everything will be automatically escaped in this block
+        using the HTML strategy
+    {% endautoescape %}
+
+    {% autoescape 'html' %}
+        Everything will be automatically escaped in this block
+        using the HTML strategy
+    {% endautoescape %}
+
+    {% autoescape 'js' %}
+        Everything will be automatically escaped in this block
+        using the js escaping strategy
+    {% endautoescape %}
+
+    {% autoescape false %}
+        Everything will be outputted as is in this block
+    {% endautoescape %}
+
+.. note::
+
+    Before Twig 1.8, the syntax was different:
+
+    .. code-block:: jinja
+
+        {% autoescape true %}
+            Everything will be automatically escaped in this block
+            using the HTML strategy
+        {% endautoescape %}
+
+        {% autoescape false %}
+            Everything will be outputted as is in this block
+        {% endautoescape %}
+
+        {% autoescape true js %}
+            Everything will be automatically escaped in this block
+            using the js escaping strategy
+        {% endautoescape %}
+
+When automatic escaping is enabled everything is escaped by default except for
+values explicitly marked as safe. Those can be marked in the template by using
+the :doc:`raw<../filters/raw>` filter:
+
+.. code-block:: jinja
+
+    {% autoescape %}
+        {{ safe_value|raw }}
+    {% endautoescape %}
+
+Functions returning template data (like :doc:`macros<macro>` and
+:doc:`parent<../functions/parent>`) always return safe markup.
+
+.. note::
+
+    Twig is smart enough to not escape an already escaped value by the
+    :doc:`escape<../filters/escape>` filter.
+
+.. note::
+
+    The chapter :doc:`Twig for Developers<../api>` gives more information
+    about when and how automatic escaping is applied.
diff --git a/vendor/twig/twig/doc/tags/block.rst b/vendor/twig/twig/doc/tags/block.rst
new file mode 100644 (file)
index 0000000..e380482
--- /dev/null
@@ -0,0 +1,11 @@
+``block``
+=========
+
+Blocks are used for inheritance and act as placeholders and replacements at
+the same time. They are documented in detail in the documentation for the
+:doc:`extends<../tags/extends>` tag.
+
+Block names should consist of alphanumeric characters, and underscores. Dashes
+are not permitted.
+
+.. seealso:: :doc:`block<../functions/block>`, :doc:`parent<../functions/parent>`, :doc:`use<../tags/use>`, :doc:`extends<../tags/extends>`
diff --git a/vendor/twig/twig/doc/tags/do.rst b/vendor/twig/twig/doc/tags/do.rst
new file mode 100644 (file)
index 0000000..eca63d0
--- /dev/null
@@ -0,0 +1,12 @@
+``do``
+======
+
+.. versionadded:: 1.5
+    The do tag was added in Twig 1.5.
+
+The ``do`` tag works exactly like the regular variable expression (``{{ ...
+}}``) just that it doesn't print anything:
+
+.. code-block:: jinja
+
+    {% do 1 + 2 %}
diff --git a/vendor/twig/twig/doc/tags/embed.rst b/vendor/twig/twig/doc/tags/embed.rst
new file mode 100644 (file)
index 0000000..5a6a029
--- /dev/null
@@ -0,0 +1,178 @@
+``embed``
+=========
+
+.. versionadded:: 1.8
+    The ``embed`` tag was added in Twig 1.8.
+
+The ``embed`` tag combines the behaviour of :doc:`include<include>` and
+:doc:`extends<extends>`.
+It allows you to include another template's contents, just like ``include``
+does. But it also allows you to override any block defined inside the
+included template, like when extending a template.
+
+Think of an embedded template as a "micro layout skeleton".
+
+.. code-block:: jinja
+
+    {% embed "teasers_skeleton.twig" %}
+        {# These blocks are defined in "teasers_skeleton.twig" #}
+        {# and we override them right here:                    #}
+        {% block left_teaser %}
+            Some content for the left teaser box
+        {% endblock %}
+        {% block right_teaser %}
+            Some content for the right teaser box
+        {% endblock %}
+    {% endembed %}
+
+The ``embed`` tag takes the idea of template inheritance to the level of
+content fragments. While template inheritance allows for "document skeletons",
+which are filled with life by child templates, the ``embed`` tag allows you to
+create "skeletons" for smaller units of content and re-use and fill them
+anywhere you like.
+
+Since the use case may not be obvious, let's look at a simplified example.
+Imagine a base template shared by multiple HTML pages, defining a single block
+named "content":
+
+.. code-block:: text
+
+    ┌─── page layout ─────────────────────┐
+    │                                     │
+    │           ┌── block "content" ──┐   │
+    │           │                     │   │
+    │           │                     │   │
+    │           │ (child template to  │   │
+    │           │  put content here)  │   │
+    │           │                     │   │
+    │           │                     │   │
+    │           └─────────────────────┘   │
+    │                                     │
+    └─────────────────────────────────────┘
+
+Some pages ("foo" and "bar") share the same content structure -
+two vertically stacked boxes:
+
+.. code-block:: text
+
+    ┌─── page layout ─────────────────────┐
+    │                                     │
+    │           ┌── block "content" ──┐   │
+    │           │ ┌─ block "top" ───┐ │   │
+    │           │ │                 │ │   │
+    │           │ └─────────────────┘ │   │
+    │           │ ┌─ block "bottom" ┐ │   │
+    │           │ │                 │ │   │
+    │           │ └─────────────────┘ │   │
+    │           └─────────────────────┘   │
+    │                                     │
+    └─────────────────────────────────────┘
+
+While other pages ("boom" and "baz") share a different content structure -
+two boxes side by side:
+
+.. code-block:: text
+
+    ┌─── page layout ─────────────────────┐
+    │                                     │
+    │           ┌── block "content" ──┐   │
+    │           │                     │   │    
+    │           │ ┌ block ┐ ┌ block ┐ │   │
+    │           │ │"left" │ │"right"│ │   │
+    │           │ │       │ │       │ │   │
+    │           │ │       │ │       │ │   │
+    │           │ └───────┘ └───────┘ │   │
+    │           └─────────────────────┘   │
+    │                                     │
+    └─────────────────────────────────────┘
+
+Without the ``embed`` tag, you have two ways to design your templates:
+
+ * Create two "intermediate" base templates that extend the master layout
+   template: one with vertically stacked boxes to be used by the "foo" and
+   "bar" pages and another one with side-by-side boxes for the "boom" and
+   "baz" pages.
+
+ * Embed the markup for the top/bottom and left/right boxes into each page 
+   template directly.
+
+These two solutions do not scale well because they each have a major drawback:
+
+ * The first solution may indeed work for this simplified example. But imagine
+   we add a sidebar, which may again contain different, recurring structures
+   of content. Now we would need to create intermediate base templates for
+   all occurring combinations of content structure and sidebar structure...
+   and so on.
+
+ * The second solution involves duplication of common code with all its negative
+   consequences: any change involves finding and editing all affected copies
+   of the structure, correctness has to be verified for each copy, copies may
+   go out of sync by careless modifications etc.
+
+In such a situation, the ``embed`` tag comes in handy. The common layout
+code can live in a single base template, and the two different content structures,
+let's call them "micro layouts" go into separate templates which are embedded
+as necessary:
+
+Page template ``foo.twig``:
+
+.. code-block:: jinja
+
+    {% extends "layout_skeleton.twig" %}
+
+    {% block content %}
+        {% embed "vertical_boxes_skeleton.twig" %}
+            {% block top %}
+                Some content for the top box
+            {% endblock %}
+
+            {% block bottom %}
+                Some content for the bottom box
+            {% endblock %}
+        {% endembed %}
+    {% endblock %}
+
+And here is the code for ``vertical_boxes_skeleton.twig``:
+
+.. code-block:: html+jinja
+
+    <div class="top_box">
+        {% block top %}
+            Top box default content
+        {% endblock %}
+    </div>
+
+    <div class="bottom_box">
+        {% block bottom %}
+            Bottom box default content
+        {% endblock %}
+    </div>
+
+The goal of the ``vertical_boxes_skeleton.twig`` template being to factor
+out the HTML markup for the boxes.
+
+The ``embed`` tag takes the exact same arguments as the ``include`` tag:
+
+.. code-block:: jinja
+
+    {% embed "base" with {'foo': 'bar'} %}
+        ...
+    {% endembed %}
+
+    {% embed "base" with {'foo': 'bar'} only %}
+        ...
+    {% endembed %}
+
+    {% embed "base" ignore missing %}
+        ...
+    {% endembed %}
+
+.. warning::
+
+    As embedded templates do not have "names", auto-escaping strategies based
+    on the template "filename" won't work as expected if you change the
+    context (for instance, if you embed a CSS/JavaScript template into an HTML
+    one). In that case, explicitly set the default auto-escaping strategy with
+    the ``autoescape`` tag.
+
+.. seealso:: :doc:`include<../tags/include>`
diff --git a/vendor/twig/twig/doc/tags/extends.rst b/vendor/twig/twig/doc/tags/extends.rst
new file mode 100644 (file)
index 0000000..f995a5d
--- /dev/null
@@ -0,0 +1,268 @@
+``extends``
+===========
+
+The ``extends`` tag can be used to extend a template from another one.
+
+.. note::
+
+    Like PHP, Twig does not support multiple inheritance. So you can only have
+    one extends tag called per rendering. However, Twig supports horizontal
+    :doc:`reuse<use>`.
+
+Let's define a base template, ``base.html``, which defines a simple HTML
+skeleton document:
+
+.. code-block:: html+jinja
+
+    <!DOCTYPE html>
+    <html>
+        <head>
+            {% block head %}
+                <link rel="stylesheet" href="style.css" />
+                <title>{% block title %}{% endblock %} - My Webpage</title>
+            {% endblock %}
+        </head>
+        <body>
+            <div id="content">{% block content %}{% endblock %}</div>
+            <div id="footer">
+                {% block footer %}
+                    &copy; Copyright 2011 by <a href="http://domain.invalid/">you</a>.
+                {% endblock %}
+            </div>
+        </body>
+    </html>
+
+In this example, the :doc:`block<block>` tags define four blocks that child
+templates can fill in.
+
+All the ``block`` tag does is to tell the template engine that a child
+template may override those portions of the template.
+
+Child Template
+--------------
+
+A child template might look like this:
+
+.. code-block:: jinja
+
+    {% extends "base.html" %}
+
+    {% block title %}Index{% endblock %}
+    {% block head %}
+        {{ parent() }}
+        <style type="text/css">
+            .important { color: #336699; }
+        </style>
+    {% endblock %}
+    {% block content %}
+        <h1>Index</h1>
+        <p class="important">
+            Welcome on my awesome homepage.
+        </p>
+    {% endblock %}
+
+The ``extends`` tag is the key here. It tells the template engine that this
+template "extends" another template. When the template system evaluates this
+template, first it locates the parent. The extends tag should be the first tag
+in the template.
+
+Note that since the child template doesn't define the ``footer`` block, the
+value from the parent template is used instead.
+
+You can't define multiple ``block`` tags with the same name in the same
+template. This limitation exists because a block tag works in "both"
+directions. That is, a block tag doesn't just provide a hole to fill - it also
+defines the content that fills the hole in the *parent*. If there were two
+similarly-named ``block`` tags in a template, that template's parent wouldn't
+know which one of the blocks' content to use.
+
+If you want to print a block multiple times you can however use the
+``block`` function:
+
+.. code-block:: jinja
+
+    <title>{% block title %}{% endblock %}</title>
+    <h1>{{ block('title') }}</h1>
+    {% block body %}{% endblock %}
+
+Parent Blocks
+-------------
+
+It's possible to render the contents of the parent block by using the
+:doc:`parent<../functions/parent>` function. This gives back the results of
+the parent block:
+
+.. code-block:: jinja
+
+    {% block sidebar %}
+        <h3>Table Of Contents</h3>
+        ...
+        {{ parent() }}
+    {% endblock %}
+
+Named Block End-Tags
+--------------------
+
+Twig allows you to put the name of the block after the end tag for better
+readability:
+
+.. code-block:: jinja
+
+    {% block sidebar %}
+        {% block inner_sidebar %}
+            ...
+        {% endblock inner_sidebar %}
+    {% endblock sidebar %}
+
+Of course, the name after the ``endblock`` word must match the block name.
+
+Block Nesting and Scope
+-----------------------
+
+Blocks can be nested for more complex layouts. Per default, blocks have access
+to variables from outer scopes:
+
+.. code-block:: jinja
+
+    {% for item in seq %}
+        <li>{% block loop_item %}{{ item }}{% endblock %}</li>
+    {% endfor %}
+
+Block Shortcuts
+---------------
+
+For blocks with few content, it's possible to use a shortcut syntax. The
+following constructs do the same:
+
+.. code-block:: jinja
+
+    {% block title %}
+        {{ page_title|title }}
+    {% endblock %}
+
+.. code-block:: jinja
+
+    {% block title page_title|title %}
+
+Dynamic Inheritance
+-------------------
+
+Twig supports dynamic inheritance by using a variable as the base template:
+
+.. code-block:: jinja
+
+    {% extends some_var %}
+
+If the variable evaluates to a ``Twig_Template`` object, Twig will use it as
+the parent template::
+
+    // {% extends layout %}
+
+    $layout = $twig->loadTemplate('some_layout_template.twig');
+
+    $twig->display('template.twig', array('layout' => $layout));
+
+.. versionadded:: 1.2
+    The possibility to pass an array of templates has been added in Twig 1.2.
+
+You can also provide a list of templates that are checked for existence. The
+first template that exists will be used as a parent:
+
+.. code-block:: jinja
+
+    {% extends ['layout.html', 'base_layout.html'] %}
+
+Conditional Inheritance
+-----------------------
+
+As the template name for the parent can be any valid Twig expression, it's
+possible to make the inheritance mechanism conditional:
+
+.. code-block:: jinja
+
+    {% extends standalone ? "minimum.html" : "base.html" %}
+
+In this example, the template will extend the "minimum.html" layout template
+if the ``standalone`` variable evaluates to ``true``, and "base.html"
+otherwise.
+
+How blocks work?
+----------------
+
+A block provides a way to change how a certain part of a template is rendered
+but it does not interfere in any way with the logic around it.
+
+Let's take the following example to illustrate how a block work and more
+importantly, how it does not work:
+
+.. code-block:: jinja
+
+    {# base.twig #}
+
+    {% for post in posts %}
+        {% block post %}
+            <h1>{{ post.title }}</h1>
+            <p>{{ post.body }}</p>
+        {% endblock %}
+    {% endfor %}
+
+If you render this template, the result would be exactly the same with or
+without the ``block`` tag. The ``block`` inside the ``for`` loop is just a way
+to make it overridable by a child template:
+
+.. code-block:: jinja
+
+    {# child.twig #}
+
+    {% extends "base.twig" %}
+
+    {% block post %}
+        <article>
+            <header>{{ post.title }}</header>
+            <section>{{ post.text }}</section>
+        </article>
+    {% endblock %}
+
+Now, when rendering the child template, the loop is going to use the block
+defined in the child template instead of the one defined in the base one; the
+executed template is then equivalent to the following one:
+
+.. code-block:: jinja
+
+    {% for post in posts %}
+        <article>
+            <header>{{ post.title }}</header>
+            <section>{{ post.text }}</section>
+        </article>
+    {% endfor %}
+
+Let's take another example: a block included within an ``if`` statement:
+
+.. code-block:: jinja
+
+    {% if posts is empty %}
+        {% block head %}
+            {{ parent() }}
+
+            <meta name="robots" content="noindex, follow">
+        {% endblock head %}
+    {% endif %}
+
+Contrary to what you might think, this template does not define a block
+conditionally; it just makes overridable by a child template the output of
+what will be rendered when the condition is ``true``.
+
+If you want the output to be displayed conditionally, use the following
+instead:
+
+.. code-block:: jinja
+
+    {% block head %}
+        {{ parent() }}
+
+        {% if posts is empty %}
+            <meta name="robots" content="noindex, follow">
+        {% endif %}
+    {% endblock head %}
+
+.. seealso:: :doc:`block<../functions/block>`, :doc:`block<../tags/block>`, :doc:`parent<../functions/parent>`, :doc:`use<../tags/use>`
diff --git a/vendor/twig/twig/doc/tags/filter.rst b/vendor/twig/twig/doc/tags/filter.rst
new file mode 100644 (file)
index 0000000..82ca5c6
--- /dev/null
@@ -0,0 +1,21 @@
+``filter``
+==========
+
+Filter sections allow you to apply regular Twig filters on a block of template
+data. Just wrap the code in the special ``filter`` section:
+
+.. code-block:: jinja
+
+    {% filter upper %}
+        This text becomes uppercase
+    {% endfilter %}
+
+You can also chain filters:
+
+.. code-block:: jinja
+
+    {% filter lower|escape %}
+        <strong>SOME TEXT</strong>
+    {% endfilter %}
+
+    {# outputs "&lt;strong&gt;some text&lt;/strong&gt;" #}
diff --git a/vendor/twig/twig/doc/tags/flush.rst b/vendor/twig/twig/doc/tags/flush.rst
new file mode 100644 (file)
index 0000000..55ef593
--- /dev/null
@@ -0,0 +1,17 @@
+``flush``
+=========
+
+.. versionadded:: 1.5
+    The flush tag was added in Twig 1.5.
+
+The ``flush`` tag tells Twig to flush the output buffer:
+
+.. code-block:: jinja
+
+    {% flush %}
+
+.. note::
+
+    Internally, Twig uses the PHP `flush`_ function.
+
+.. _`flush`: http://php.net/flush
diff --git a/vendor/twig/twig/doc/tags/for.rst b/vendor/twig/twig/doc/tags/for.rst
new file mode 100644 (file)
index 0000000..0673b55
--- /dev/null
@@ -0,0 +1,172 @@
+``for``
+=======
+
+Loop over each item in a sequence. For example, to display a list of users
+provided in a variable called ``users``:
+
+.. code-block:: jinja
+
+    <h1>Members</h1>
+    <ul>
+        {% for user in users %}
+            <li>{{ user.username|e }}</li>
+        {% endfor %}
+    </ul>
+
+.. note::
+
+    A sequence can be either an array or an object implementing the
+    ``Traversable`` interface.
+
+If you do need to iterate over a sequence of numbers, you can use the ``..``
+operator:
+
+.. code-block:: jinja
+
+    {% for i in 0..10 %}
+        * {{ i }}
+    {% endfor %}
+
+The above snippet of code would print all numbers from 0 to 10.
+
+It can be also useful with letters:
+
+.. code-block:: jinja
+
+    {% for letter in 'a'..'z' %}
+        * {{ letter }}
+    {% endfor %}
+
+The ``..`` operator can take any expression at both sides:
+
+.. code-block:: jinja
+
+    {% for letter in 'a'|upper..'z'|upper %}
+        * {{ letter }}
+    {% endfor %}
+
+.. tip:
+
+    If you need a step different from 1, you can use the ``range`` function
+    instead.
+
+The `loop` variable
+-------------------
+
+Inside of a ``for`` loop block you can access some special variables:
+
+===================== =============================================================
+Variable              Description
+===================== =============================================================
+``loop.index``        The current iteration of the loop. (1 indexed)
+``loop.index0``       The current iteration of the loop. (0 indexed)
+``loop.revindex``     The number of iterations from the end of the loop (1 indexed)
+``loop.revindex0``    The number of iterations from the end of the loop (0 indexed)
+``loop.first``        True if first iteration
+``loop.last``         True if last iteration
+``loop.length``       The number of items in the sequence
+``loop.parent``       The parent context
+===================== =============================================================
+
+.. code-block:: jinja
+
+    {% for user in users %}
+        {{ loop.index }} - {{ user.username }}
+    {% endfor %}
+
+.. note::
+
+    The ``loop.length``, ``loop.revindex``, ``loop.revindex0``, and
+    ``loop.last`` variables are only available for PHP arrays, or objects that
+    implement the ``Countable`` interface. They are also not available when
+    looping with a condition.
+
+.. versionadded:: 1.2
+    The ``if`` modifier support has been added in Twig 1.2.
+
+Adding a condition
+------------------
+
+Unlike in PHP, it's not possible to ``break`` or ``continue`` in a loop. You
+can however filter the sequence during iteration which allows you to skip
+items. The following example skips all the users which are not active:
+
+.. code-block:: jinja
+
+    <ul>
+        {% for user in users if user.active %}
+            <li>{{ user.username|e }}</li>
+        {% endfor %}
+    </ul>
+
+The advantage is that the special loop variable will count correctly thus not
+counting the users not iterated over. Keep in mind that properties like
+``loop.last`` will not be defined when using loop conditions.
+
+.. note::
+
+    Using the ``loop`` variable within the condition is not recommended as it
+    will probably not be doing what you expect it to. For instance, adding a
+    condition like ``loop.index > 4`` won't work as the index is only
+    incremented when the condition is true (so the condition will never
+    match).
+
+The `else` Clause
+-----------------
+
+If no iteration took place because the sequence was empty, you can render a
+replacement block by using ``else``:
+
+.. code-block:: jinja
+
+    <ul>
+        {% for user in users %}
+            <li>{{ user.username|e }}</li>
+        {% else %}
+            <li><em>no user found</em></li>
+        {% endfor %}
+    </ul>
+
+Iterating over Keys
+-------------------
+
+By default, a loop iterates over the values of the sequence. You can iterate
+on keys by using the ``keys`` filter:
+
+.. code-block:: jinja
+
+    <h1>Members</h1>
+    <ul>
+        {% for key in users|keys %}
+            <li>{{ key }}</li>
+        {% endfor %}
+    </ul>
+
+Iterating over Keys and Values
+------------------------------
+
+You can also access both keys and values:
+
+.. code-block:: jinja
+
+    <h1>Members</h1>
+    <ul>
+        {% for key, user in users %}
+            <li>{{ key }}: {{ user.username|e }}</li>
+        {% endfor %}
+    </ul>
+
+Iterating over a Subset
+-----------------------
+
+You might want to iterate over a subset of values. This can be achieved using
+the :doc:`slice <../filters/slice>` filter:
+
+.. code-block:: jinja
+
+    <h1>Top Ten Members</h1>
+    <ul>
+        {% for user in users|slice(0, 10) %}
+            <li>{{ user.username|e }}</li>
+        {% endfor %}
+    </ul>
diff --git a/vendor/twig/twig/doc/tags/from.rst b/vendor/twig/twig/doc/tags/from.rst
new file mode 100644 (file)
index 0000000..5337a23
--- /dev/null
@@ -0,0 +1,8 @@
+``from``
+========
+
+The ``from`` tags import :doc:`macro<../tags/macro>` names into the current
+namespace. The tag is documented in detail in the documentation for the
+:doc:`import<../tags/import>` tag.
+
+.. seealso:: :doc:`macro<../tags/macro>`, :doc:`import<../tags/import>`
diff --git a/vendor/twig/twig/doc/tags/if.rst b/vendor/twig/twig/doc/tags/if.rst
new file mode 100644 (file)
index 0000000..d7a1451
--- /dev/null
@@ -0,0 +1,43 @@
+``if``
+======
+
+The ``if`` statement in Twig is comparable with the if statements of PHP.
+
+In the simplest form you can use it to test if an expression evaluates to
+``true``:
+
+.. code-block:: jinja
+
+    {% if online == false %}
+        <p>Our website is in maintenance mode. Please, come back later.</p>
+    {% endif %}
+
+You can also test if an array is not empty:
+
+.. code-block:: jinja
+
+    {% if users %}
+        <ul>
+            {% for user in users %}
+                <li>{{ user.username|e }}</li>
+            {% endfor %}
+        </ul>
+    {% endif %}
+
+.. note::
+
+    If you want to test if the variable is defined, use ``if users is
+    defined`` instead.
+
+For multiple branches ``elseif`` and ``else`` can be used like in PHP. You can use
+more complex ``expressions`` there too:
+
+.. code-block:: jinja
+
+    {% if kenny.sick %}
+        Kenny is sick.
+    {% elseif kenny.dead %}
+        You killed Kenny! You bastard!!!
+    {% else %}
+        Kenny looks okay --- so far
+    {% endif %}
diff --git a/vendor/twig/twig/doc/tags/import.rst b/vendor/twig/twig/doc/tags/import.rst
new file mode 100644 (file)
index 0000000..f6bf718
--- /dev/null
@@ -0,0 +1,57 @@
+``import``
+==========
+
+Twig supports putting often used code into :doc:`macros<../tags/macro>`. These
+macros can go into different templates and get imported from there.
+
+There are two ways to import templates. You can import the complete template
+into a variable or request specific macros from it.
+
+Imagine we have a helper module that renders forms (called ``forms.html``):
+
+.. code-block:: jinja
+
+    {% macro input(name, value, type, size) %}
+        <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
+    {% endmacro %}
+
+    {% macro textarea(name, value, rows) %}
+        <textarea name="{{ name }}" rows="{{ rows|default(10) }}" cols="{{ cols|default(40) }}">{{ value|e }}</textarea>
+    {% endmacro %}
+
+The easiest and most flexible is importing the whole module into a variable.
+That way you can access the attributes:
+
+.. code-block:: jinja
+
+    {% import 'forms.html' as forms %}
+
+    <dl>
+        <dt>Username</dt>
+        <dd>{{ forms.input('username') }}</dd>
+        <dt>Password</dt>
+        <dd>{{ forms.input('password', null, 'password') }}</dd>
+    </dl>
+    <p>{{ forms.textarea('comment') }}</p>
+
+Alternatively you can import names from the template into the current
+namespace:
+
+.. code-block:: jinja
+
+    {% from 'forms.html' import input as input_field, textarea %}
+
+    <dl>
+        <dt>Username</dt>
+        <dd>{{ input_field('username') }}</dd>
+        <dt>Password</dt>
+        <dd>{{ input_field('password', '', 'password') }}</dd>
+    </dl>
+    <p>{{ textarea('comment') }}</p>
+
+.. tip::
+
+    To import macros from the current file, use the special ``_self`` variable
+    for the source.
+
+.. seealso:: :doc:`macro<../tags/macro>`, :doc:`from<../tags/from>`
diff --git a/vendor/twig/twig/doc/tags/include.rst b/vendor/twig/twig/doc/tags/include.rst
new file mode 100644 (file)
index 0000000..10b262d
--- /dev/null
@@ -0,0 +1,86 @@
+``include``
+===========
+
+The ``include`` statement includes a template and return the rendered content
+of that file into the current namespace:
+
+.. code-block:: jinja
+
+    {% include 'header.html' %}
+        Body
+    {% include 'footer.html' %}
+
+Included templates have access to the variables of the active context.
+
+If you are using the filesystem loader, the templates are looked for in the
+paths defined by it.
+
+You can add additional variables by passing them after the ``with`` keyword:
+
+.. code-block:: jinja
+
+    {# template.html will have access to the variables from the current context and the additional ones provided #}
+    {% include 'template.html' with {'foo': 'bar'} %}
+
+    {% set vars = {'foo': 'bar'} %}
+    {% include 'template.html' with vars %}
+
+You can disable access to the context by appending the ``only`` keyword:
+
+.. code-block:: jinja
+
+    {# only the foo variable will be accessible #}
+    {% include 'template.html' with {'foo': 'bar'} only %}
+
+.. code-block:: jinja
+
+    {# no variables will be accessible #}
+    {% include 'template.html' only %}
+
+.. tip::
+
+    When including a template created by an end user, you should consider
+    sandboxing it. More information in the :doc:`Twig for Developers<../api>`
+    chapter and in the :doc:`sandbox<../tags/sandbox>` tag documentation.
+
+The template name can be any valid Twig expression:
+
+.. code-block:: jinja
+
+    {% include some_var %}
+    {% include ajax ? 'ajax.html' : 'not_ajax.html' %}
+
+And if the expression evaluates to a ``Twig_Template`` object, Twig will use it
+directly::
+
+    // {% include template %}
+
+    $template = $twig->loadTemplate('some_template.twig');
+
+    $twig->loadTemplate('template.twig')->display(array('template' => $template));
+
+.. versionadded:: 1.2
+    The ``ignore missing`` feature has been added in Twig 1.2.
+
+You can mark an include with ``ignore missing`` in which case Twig will ignore
+the statement if the template to be included does not exist. It has to be
+placed just after the template name. Here some valid examples:
+
+.. code-block:: jinja
+
+    {% include 'sidebar.html' ignore missing %}
+    {% include 'sidebar.html' ignore missing with {'foo': 'bar'} %}
+    {% include 'sidebar.html' ignore missing only %}
+
+.. versionadded:: 1.2
+    The possibility to pass an array of templates has been added in Twig 1.2.
+
+You can also provide a list of templates that are checked for existence before
+inclusion. The first template that exists will be included:
+
+.. code-block:: jinja
+
+    {% include ['page_detailed.html', 'page.html'] %}
+
+If ``ignore missing`` is given, it will fall back to rendering nothing if none
+of the templates exist, otherwise it will throw an exception.
diff --git a/vendor/twig/twig/doc/tags/index.rst b/vendor/twig/twig/doc/tags/index.rst
new file mode 100644 (file)
index 0000000..64e8864
--- /dev/null
@@ -0,0 +1,24 @@
+Tags
+====
+
+.. toctree::
+    :maxdepth: 1
+
+    autoescape
+    block
+    filter
+    do
+    embed
+    extends
+    flush
+    for
+    from
+    if
+    import
+    include
+    macro
+    sandbox
+    set
+    spaceless
+    use
+    verbatim
diff --git a/vendor/twig/twig/doc/tags/macro.rst b/vendor/twig/twig/doc/tags/macro.rst
new file mode 100644 (file)
index 0000000..11c115a
--- /dev/null
@@ -0,0 +1,83 @@
+``macro``
+=========
+
+Macros are comparable with functions in regular programming languages. They
+are useful to put often used HTML idioms into reusable elements to not repeat
+yourself.
+
+Here is a small example of a macro that renders a form element:
+
+.. code-block:: jinja
+
+    {% macro input(name, value, type, size) %}
+        <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
+    {% endmacro %}
+
+Macros differs from native PHP functions in a few ways:
+
+* Default argument values are defined by using the ``default`` filter in the
+  macro body;
+
+* Arguments of a macro are always optional.
+
+But as with PHP functions, macros don't have access to the current template
+variables.
+
+.. tip::
+
+    You can pass the whole context as an argument by using the special
+    ``_context`` variable.
+
+Macros can be defined in any template, and need to be "imported" before being
+used (see the documentation for the :doc:`import<../tags/import>` tag for more
+information):
+
+.. code-block:: jinja
+
+    {% import "forms.html" as forms %}
+
+The above ``import`` call imports the "forms.html" file (which can contain only
+macros, or a template and some macros), and import the functions as items of
+the ``forms`` variable.
+
+The macro can then be called at will:
+
+.. code-block:: jinja
+
+    <p>{{ forms.input('username') }}</p>
+    <p>{{ forms.input('password', null, 'password') }}</p>
+
+If macros are defined and used in the same template, you can use the
+special ``_self`` variable to import them:
+
+.. code-block:: jinja
+
+    {% import _self as forms %}
+
+    <p>{{ forms.input('username') }}</p>
+
+.. warning::
+
+    When you define a macro in the template where you are going to use it, you
+    might be tempted to call the macro directly via ``_self.input()`` instead
+    of importing it; even if seems to work, this is just a side-effect of the
+    current implementation and it won't work anymore in Twig 2.x.
+
+When you want to use a macro in another macro from the same file, you need to
+import it locally:
+
+.. code-block:: jinja
+
+    {% macro input(name, value, type, size) %}
+        <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
+    {% endmacro %}
+
+    {% macro wrapped_input(name, value, type, size) %}
+        {% import _self as forms %}
+
+        <div class="field">
+            {{ forms.input(name, value, type, size) }}
+        </div>
+    {% endmacro %}
+
+.. seealso:: :doc:`from<../tags/from>`, :doc:`import<../tags/import>`
diff --git a/vendor/twig/twig/doc/tags/sandbox.rst b/vendor/twig/twig/doc/tags/sandbox.rst
new file mode 100644 (file)
index 0000000..e186726
--- /dev/null
@@ -0,0 +1,30 @@
+``sandbox``
+===========
+
+The ``sandbox`` tag can be used to enable the sandboxing mode for an included
+template, when sandboxing is not enabled globally for the Twig environment:
+
+.. code-block:: jinja
+
+    {% sandbox %}
+        {% include 'user.html' %}
+    {% endsandbox %}
+
+.. warning::
+
+    The ``sandbox`` tag is only available when the sandbox extension is
+    enabled (see the :doc:`Twig for Developers<../api>` chapter).
+
+.. note::
+
+    The ``sandbox`` tag can only be used to sandbox an include tag and it
+    cannot be used to sandbox a section of a template. The following example
+    won't work:
+
+    .. code-block:: jinja
+
+        {% sandbox %}
+            {% for i in 1..2 %}
+                {{ i }}
+            {% endfor %}
+        {% endsandbox %}
diff --git a/vendor/twig/twig/doc/tags/set.rst b/vendor/twig/twig/doc/tags/set.rst
new file mode 100644 (file)
index 0000000..3eba239
--- /dev/null
@@ -0,0 +1,78 @@
+``set``
+=======
+
+Inside code blocks you can also assign values to variables. Assignments use
+the ``set`` tag and can have multiple targets.
+
+Here is how you can assign the ``bar`` value to the ``foo`` variable:
+
+.. code-block:: jinja
+
+    {% set foo = 'bar' %}
+
+After the ``set`` call, the ``foo`` variable is available in the template like
+any other ones:
+
+.. code-block:: jinja
+
+    {# displays bar #}
+    {{ foo }}
+
+The assigned value can be any valid :ref:`Twig expressions
+<twig-expressions>`:
+
+.. code-block:: jinja
+
+    {% set foo = [1, 2] %}
+    {% set foo = {'foo': 'bar'} %}
+    {% set foo = 'foo' ~ 'bar' %}
+
+Several variables can be assigned in one block:
+
+.. code-block:: jinja
+
+    {% set foo, bar = 'foo', 'bar' %}
+
+    {# is equivalent to #}
+
+    {% set foo = 'foo' %}
+    {% set bar = 'bar' %}
+
+The ``set`` tag can also be used to 'capture' chunks of text:
+
+.. code-block:: jinja
+
+    {% set foo %}
+        <div id="pagination">
+            ...
+        </div>
+    {% endset %}
+
+.. caution::
+
+    If you enable automatic output escaping, Twig will only consider the
+    content to be safe when capturing chunks of text.
+
+.. note::
+
+    Note that loops are scoped in Twig; therefore a variable declared inside a
+    ``for`` loop is not accessible outside the loop itself:
+
+    .. code-block:: jinja
+
+        {% for item in list %}
+            {% set foo = item %}
+        {% endfor %}
+
+        {# foo is NOT available #}
+
+    If you want to access the variable, just declare it before the loop:
+
+    .. code-block:: jinja
+
+        {% set foo = "" %}
+        {% for item in list %}
+            {% set foo = item %}
+        {% endfor %}
+
+        {# foo is available #}
diff --git a/vendor/twig/twig/doc/tags/spaceless.rst b/vendor/twig/twig/doc/tags/spaceless.rst
new file mode 100644 (file)
index 0000000..12e77b2
--- /dev/null
@@ -0,0 +1,37 @@
+``spaceless``
+=============
+
+Use the ``spaceless`` tag to remove whitespace *between HTML tags*, not
+whitespace within HTML tags or whitespace in plain text:
+
+.. code-block:: jinja
+
+    {% spaceless %}
+        <div>
+            <strong>foo</strong>
+        </div>
+    {% endspaceless %}
+
+    {# output will be <div><strong>foo</strong></div> #}
+
+This tag is not meant to "optimize" the size of the generated HTML content but
+merely to avoid extra whitespace between HTML tags to avoid browser rendering
+quirks under some circumstances.
+
+.. tip::
+
+    If you want to optimize the size of the generated HTML content, gzip
+    compress the output instead.
+
+.. tip::
+
+    If you want to create a tag that actually removes all extra whitespace in
+    an HTML string, be warned that this is not as easy as it seems to be
+    (think of ``textarea`` or ``pre`` tags for instance). Using a third-party
+    library like Tidy is probably a better idea.
+
+.. tip::
+
+    For more information on whitespace control, read the
+    :doc:`dedicated<../templates>` section of the documentation and learn how
+    you can also use the whitespace control modifier on your tags.
diff --git a/vendor/twig/twig/doc/tags/use.rst b/vendor/twig/twig/doc/tags/use.rst
new file mode 100644 (file)
index 0000000..085f916
--- /dev/null
@@ -0,0 +1,123 @@
+``use``
+=======
+
+.. versionadded:: 1.1
+    Horizontal reuse was added in Twig 1.1.
+
+.. note::
+
+    Horizontal reuse is an advanced Twig feature that is hardly ever needed in
+    regular templates. It is mainly used by projects that need to make
+    template blocks reusable without using inheritance.
+
+Template inheritance is one of the most powerful Twig's feature but it is
+limited to single inheritance; a template can only extend one other template.
+This limitation makes template inheritance simple to understand and easy to
+debug:
+
+.. code-block:: jinja
+
+    {% extends "base.html" %}
+
+    {% block title %}{% endblock %}
+    {% block content %}{% endblock %}
+
+Horizontal reuse is a way to achieve the same goal as multiple inheritance,
+but without the associated complexity:
+
+.. code-block:: jinja
+
+    {% extends "base.html" %}
+
+    {% use "blocks.html" %}
+
+    {% block title %}{% endblock %}
+    {% block content %}{% endblock %}
+
+The ``use`` statement tells Twig to import the blocks defined in
+```blocks.html`` into the current template (it's like macros, but for blocks):
+
+.. code-block:: jinja
+
+    # blocks.html
+    {% block sidebar %}{% endblock %}
+
+In this example, the ``use`` statement imports the ``sidebar`` block into the
+main template. The code is mostly equivalent to the following one (the
+imported blocks are not outputted automatically):
+
+.. code-block:: jinja
+
+    {% extends "base.html" %}
+
+    {% block sidebar %}{% endblock %}
+    {% block title %}{% endblock %}
+    {% block content %}{% endblock %}
+
+.. note::
+
+    The ``use`` tag only imports a template if it does not extend another
+    template, if it does not define macros, and if the body is empty. But it
+    can *use* other templates.
+
+.. note::
+
+    Because ``use`` statements are resolved independently of the context
+    passed to the template, the template reference cannot be an expression.
+
+The main template can also override any imported block. If the template
+already defines the ``sidebar`` block, then the one defined in ``blocks.html``
+is ignored. To avoid name conflicts, you can rename imported blocks:
+
+.. code-block:: jinja
+
+    {% extends "base.html" %}
+
+    {% use "blocks.html" with sidebar as base_sidebar %}
+
+    {% block sidebar %}{% endblock %}
+    {% block title %}{% endblock %}
+    {% block content %}{% endblock %}
+
+.. versionadded:: 1.3
+    The ``parent()`` support was added in Twig 1.3.
+
+The ``parent()`` function automatically determines the correct inheritance
+tree, so it can be used when overriding a block defined in an imported
+template:
+
+.. code-block:: jinja
+
+    {% extends "base.html" %}
+
+    {% use "blocks.html" %}
+
+    {% block sidebar %}
+        {{ parent() }}
+    {% endblock %}
+
+    {% block title %}{% endblock %}
+    {% block content %}{% endblock %}
+
+In this example, ``parent()`` will correctly call the ``sidebar`` block from
+the ``blocks.html`` template.
+
+.. tip::
+
+    In Twig 1.2, renaming allows you to simulate inheritance by calling the
+    "parent" block:
+
+    .. code-block:: jinja
+
+        {% extends "base.html" %}
+
+        {% use "blocks.html" with sidebar as parent_sidebar %}
+
+        {% block sidebar %}
+            {{ block('parent_sidebar') }}
+        {% endblock %}
+
+.. note::
+
+    You can use as many ``use`` statements as you want in any given template.
+    If two imported templates define the same block, the latest one wins.
diff --git a/vendor/twig/twig/doc/tags/verbatim.rst b/vendor/twig/twig/doc/tags/verbatim.rst
new file mode 100644 (file)
index 0000000..fe61ca1
--- /dev/null
@@ -0,0 +1,24 @@
+``verbatim``
+============
+
+.. versionadded:: 1.12
+    The ``verbatim`` tag was added in Twig 1.12 (it was named ``raw`` before).
+
+The ``verbatim`` tag marks sections as being raw text that should not be
+parsed. For example to put Twig syntax as example into a template you can use
+this snippet:
+
+.. code-block:: jinja
+
+    {% verbatim %}
+        <ul>
+        {% for item in seq %}
+            <li>{{ item }}</li>
+        {% endfor %}
+        </ul>
+    {% endverbatim %}
+
+.. note::
+
+    The ``verbatim`` tag works in the exact same way as the old ``raw`` tag,
+    but was renamed to avoid confusion with the ``raw`` filter.
\ No newline at end of file
diff --git a/vendor/twig/twig/doc/templates.rst b/vendor/twig/twig/doc/templates.rst
new file mode 100644 (file)
index 0000000..542b8ae
--- /dev/null
@@ -0,0 +1,851 @@
+Twig for Template Designers
+===========================
+
+This document describes the syntax and semantics of the template engine and
+will be most useful as reference to those creating Twig templates.
+
+Synopsis
+--------
+
+A template is simply a text file. It can generate any text-based format (HTML,
+XML, CSV, LaTeX, etc.). It doesn't have a specific extension, ``.html`` or
+``.xml`` are just fine.
+
+A template contains **variables** or **expressions**, which get replaced with
+values when the template is evaluated, and **tags**, which control the logic
+of the template.
+
+Below is a minimal template that illustrates a few basics. We will cover the
+details later on:
+
+.. code-block:: html+jinja
+
+    <!DOCTYPE html>
+    <html>
+        <head>
+            <title>My Webpage</title>
+        </head>
+        <body>
+            <ul id="navigation">
+            {% for item in navigation %}
+                <li><a href="{{ item.href }}">{{ item.caption }}</a></li>
+            {% endfor %}
+            </ul>
+
+            <h1>My Webpage</h1>
+            {{ a_variable }}
+        </body>
+    </html>
+
+There are two kinds of delimiters: ``{% ... %}`` and ``{{ ... }}``. The first
+one is used to execute statements such as for-loops, the latter prints the
+result of an expression to the template.
+
+IDEs Integration
+----------------
+
+Many IDEs support syntax highlighting and auto-completion for Twig:
+
+* *Textmate* via the `Twig bundle`_
+* *Vim* via the `Jinja syntax plugin`_
+* *Netbeans* via the `Twig syntax plugin`_ (until 7.1, native as of 7.2)
+* *PhpStorm* (native as of 2.1)
+* *Eclipse* via the `Twig plugin`_
+* *Sublime Text* via the `Twig bundle`_
+* *GtkSourceView* via the `Twig language definition`_ (used by gedit and other projects)
+* *Coda* and *SubEthaEdit* via the `Twig syntax mode`_
+* *Coda 2* via the `other Twig syntax mode`_
+* *Komodo* and *Komodo Edit* via the Twig highlight/syntax check mode
+* *Notepad++* via the `Notepad++ Twig Highlighter`_
+* *Emacs* via `web-mode.el`_
+
+Variables
+---------
+
+The application passes variables to the templates you can mess around in the
+template. Variables may have attributes or elements on them you can access
+too. How a variable looks like heavily depends on the application providing
+those.
+
+You can use a dot (``.``) to access attributes of a variable (methods or
+properties of a PHP object, or items of a PHP array), or the so-called
+"subscript" syntax (``[]``):
+
+.. code-block:: jinja
+
+    {{ foo.bar }}
+    {{ foo['bar'] }}
+
+When the attribute contains special characters (like ``-`` that would be
+interpreted as the minus operator), use the ``attribute`` function instead to
+access the variable attribute:
+
+.. code-block:: jinja
+
+    {# equivalent to the non-working foo.data-foo #}
+    {{ attribute(foo, 'data-foo') }}
+
+.. note::
+
+    It's important to know that the curly braces are *not* part of the
+    variable but the print statement. If you access variables inside tags
+    don't put the braces around.
+
+If a variable or attribute does not exist, you will get back a ``null`` value
+when the ``strict_variables`` option is set to ``false``, otherwise Twig will
+throw an error (see :ref:`environment options<environment_options>`).
+
+.. sidebar:: Implementation
+
+    For convenience sake ``foo.bar`` does the following things on the PHP
+    layer:
+
+    * check if ``foo`` is an array and ``bar`` a valid element;
+    * if not, and if ``foo`` is an object, check that ``bar`` is a valid property;
+    * if not, and if ``foo`` is an object, check that ``bar`` is a valid method
+      (even if ``bar`` is the constructor - use ``__construct()`` instead);
+    * if not, and if ``foo`` is an object, check that ``getBar`` is a valid method;
+    * if not, and if ``foo`` is an object, check that ``isBar`` is a valid method;
+    * if not, return a ``null`` value.
+
+    ``foo['bar']`` on the other hand only works with PHP arrays:
+
+    * check if ``foo`` is an array and ``bar`` a valid element;
+    * if not, return a ``null`` value.
+
+.. note::
+
+    If you want to get a dynamic attribute on a variable, use the
+    :doc:`attribute<functions/attribute>` function instead.
+
+Global Variables
+~~~~~~~~~~~~~~~~
+
+The following variables are always available in templates:
+
+* ``_self``: references the current template;
+* ``_context``: references the current context;
+* ``_charset``: references the current charset.
+
+Setting Variables
+~~~~~~~~~~~~~~~~~
+
+You can assign values to variables inside code blocks. Assignments use the
+:doc:`set<tags/set>` tag:
+
+.. code-block:: jinja
+
+    {% set foo = 'foo' %}
+    {% set foo = [1, 2] %}
+    {% set foo = {'foo': 'bar'} %}
+
+Filters
+-------
+
+Variables can be modified by **filters**. Filters are separated from the
+variable by a pipe symbol (``|``) and may have optional arguments in
+parentheses. Multiple filters can be chained. The output of one filter is
+applied to the next.
+
+The following example removes all HTML tags from the ``name`` and title-cases
+it:
+
+.. code-block:: jinja
+
+    {{ name|striptags|title }}
+
+Filters that accept arguments have parentheses around the arguments. This
+example will join a list by commas:
+
+.. code-block:: jinja
+
+    {{ list|join(', ') }}
+
+To apply a filter on a section of code, wrap it with the
+:doc:`filter<tags/filter>` tag:
+
+.. code-block:: jinja
+
+    {% filter upper %}
+      This text becomes uppercase
+    {% endfilter %}
+
+Go to the :doc:`filters<filters/index>` page to learn more about the built-in
+filters.
+
+Functions
+---------
+
+Functions can be called to generate content. Functions are called by their
+name followed by parentheses (``()``) and may have arguments.
+
+For instance, the ``range`` function returns a list containing an arithmetic
+progression of integers:
+
+.. code-block:: jinja
+
+    {% for i in range(0, 3) %}
+        {{ i }},
+    {% endfor %}
+
+Go to the :doc:`functions<functions/index>` page to learn more about the
+built-in functions.
+
+Named Arguments
+---------------
+
+.. versionadded:: 1.12
+    Support for named arguments was added in Twig 1.12.
+
+Arguments for filters and functions can also be passed as *named arguments*:
+
+.. code-block:: jinja
+
+    {% for i in range(low=1, high=10, step=2) %}
+        {{ i }},
+    {% endfor %}
+
+Using named arguments makes your templates more explicit about the meaning of
+the values you pass as arguments:
+
+.. code-block:: jinja
+
+    {{ data|convert_encoding('UTF-8', 'iso-2022-jp') }}
+
+    {# versus #}
+
+    {{ data|convert_encoding(from='iso-2022-jp', to='UTF-8') }}
+
+Named arguments also allow you to skip some arguments for which you don't want
+to change the default value:
+
+.. code-block:: jinja
+
+    {# the first argument is the date format, which defaults to the global date format if null is passed #}
+    {{ "now"|date(null, "Europe/Paris") }}
+
+    {# or skip the format value by using a named argument for the timezone #}
+    {{ "now"|date(timezone="Europe/Paris") }}
+
+You can also use both positional and named arguments in one call, in which
+case positional arguments must always come before named arguments:
+
+.. code-block:: jinja
+
+    {{ "now"|date('d/m/Y H:i', timezone="Europe/Paris") }}
+
+.. tip::
+
+    Each function and filter documentation page has a section where the names
+    of all arguments are listed when supported.
+
+Control Structure
+-----------------
+
+A control structure refers to all those things that control the flow of a
+program - conditionals (i.e. ``if``/``elseif``/``else``), ``for``-loops, as
+well as things like blocks. Control structures appear inside ``{% ... %}``
+blocks.
+
+For example, to display a list of users provided in a variable called
+``users``, use the :doc:`for<tags/for>` tag:
+
+.. code-block:: jinja
+
+    <h1>Members</h1>
+    <ul>
+        {% for user in users %}
+            <li>{{ user.username|e }}</li>
+        {% endfor %}
+    </ul>
+
+The :doc:`if<tags/if>` tag can be used to test an expression:
+
+.. code-block:: jinja
+
+    {% if users|length > 0 %}
+        <ul>
+            {% for user in users %}
+                <li>{{ user.username|e }}</li>
+            {% endfor %}
+        </ul>
+    {% endif %}
+
+Go to the :doc:`tags<tags/index>` page to learn more about the built-in tags.
+
+Comments
+--------
+
+To comment-out part of a line in a template, use the comment syntax ``{# ...
+#}``. This is useful for debugging or to add information for other template
+designers or yourself:
+
+.. code-block:: jinja
+
+    {# note: disabled template because we no longer use this
+        {% for user in users %}
+            ...
+        {% endfor %}
+    #}
+
+Including other Templates
+-------------------------
+
+The :doc:`include<tags/include>` tag is useful to include a template and
+return the rendered content of that template into the current one:
+
+.. code-block:: jinja
+
+    {% include 'sidebar.html' %}
+
+Per default included templates are passed the current context.
+
+The context that is passed to the included template includes variables defined
+in the template:
+
+.. code-block:: jinja
+
+    {% for box in boxes %}
+        {% include "render_box.html" %}
+    {% endfor %}
+
+The included template ``render_box.html`` is able to access ``box``.
+
+The filename of the template depends on the template loader. For instance, the
+``Twig_Loader_Filesystem`` allows you to access other templates by giving the
+filename. You can access templates in subdirectories with a slash:
+
+.. code-block:: jinja
+
+    {% include "sections/articles/sidebar.html" %}
+
+This behavior depends on the application embedding Twig.
+
+Template Inheritance
+--------------------
+
+The most powerful part of Twig is template inheritance. Template inheritance
+allows you to build a base "skeleton" template that contains all the common
+elements of your site and defines **blocks** that child templates can
+override.
+
+Sounds complicated but is very basic. It's easier to understand it by
+starting with an example.
+
+Let's define a base template, ``base.html``, which defines a simple HTML
+skeleton document that you might use for a simple two-column page:
+
+.. code-block:: html+jinja
+
+    <!DOCTYPE html>
+    <html>
+        <head>
+            {% block head %}
+                <link rel="stylesheet" href="style.css" />
+                <title>{% block title %}{% endblock %} - My Webpage</title>
+            {% endblock %}
+        </head>
+        <body>
+            <div id="content">{% block content %}{% endblock %}</div>
+            <div id="footer">
+                {% block footer %}
+                    &copy; Copyright 2011 by <a href="http://domain.invalid/">you</a>.
+                {% endblock %}
+            </div>
+        </body>
+    </html>
+
+In this example, the :doc:`block<tags/block>` tags define four blocks that
+child templates can fill in. All the ``block`` tag does is to tell the
+template engine that a child template may override those portions of the
+template.
+
+A child template might look like this:
+
+.. code-block:: jinja
+
+    {% extends "base.html" %}
+
+    {% block title %}Index{% endblock %}
+    {% block head %}
+        {{ parent() }}
+        <style type="text/css">
+            .important { color: #336699; }
+        </style>
+    {% endblock %}
+    {% block content %}
+        <h1>Index</h1>
+        <p class="important">
+            Welcome to my awesome homepage.
+        </p>
+    {% endblock %}
+
+The :doc:`extends<tags/extends>` tag is the key here. It tells the template
+engine that this template "extends" another template. When the template system
+evaluates this template, first it locates the parent. The extends tag should
+be the first tag in the template.
+
+Note that since the child template doesn't define the ``footer`` block, the
+value from the parent template is used instead.
+
+It's possible to render the contents of the parent block by using the
+:doc:`parent<functions/parent>` function. This gives back the results of the
+parent block:
+
+.. code-block:: jinja
+
+    {% block sidebar %}
+        <h3>Table Of Contents</h3>
+        ...
+        {{ parent() }}
+    {% endblock %}
+
+.. tip::
+
+    The documentation page for the :doc:`extends<tags/extends>` tag describes
+    more advanced features like block nesting, scope, dynamic inheritance, and
+    conditional inheritance.
+
+.. note::
+
+    Twig also supports multiple inheritance with the so called horizontal reuse
+    with the help of the :doc:`use<tags/use>` tag. This is an advanced feature
+    hardly ever needed in regular templates.
+
+HTML Escaping
+-------------
+
+When generating HTML from templates, there's always a risk that a variable
+will include characters that affect the resulting HTML. There are two
+approaches: manually escaping each variable or automatically escaping
+everything by default.
+
+Twig supports both, automatic escaping is enabled by default.
+
+.. note::
+
+    Automatic escaping is only supported if the *escaper* extension has been
+    enabled (which is the default).
+
+Working with Manual Escaping
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If manual escaping is enabled, it is **your** responsibility to escape
+variables if needed. What to escape? Any variable you don't trust.
+
+Escaping works by piping the variable through the
+:doc:`escape<filters/escape>` or ``e`` filter:
+
+.. code-block:: jinja
+
+    {{ user.username|e }}
+
+By default, the ``escape`` filter uses the ``html`` strategy, but depending on
+the escaping context, you might want to explicitly use any other available
+strategies:
+
+.. code-block:: jinja
+
+    {{ user.username|e('js') }}
+    {{ user.username|e('css') }}
+    {{ user.username|e('url') }}
+    {{ user.username|e('html_attr') }}
+
+Working with Automatic Escaping
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Whether automatic escaping is enabled or not, you can mark a section of a
+template to be escaped or not by using the :doc:`autoescape<tags/autoescape>`
+tag:
+
+.. code-block:: jinja
+
+    {% autoescape %}
+        Everything will be automatically escaped in this block (using the HTML strategy)
+    {% endautoescape %}
+
+By default, auto-escaping uses the ``html`` escaping strategy. If you output
+variables in other contexts, you need to explicitly escape them with the
+appropriate escaping strategy:
+
+.. code-block:: jinja
+
+    {% autoescape 'js' %}
+        Everything will be automatically escaped in this block (using the JS strategy)
+    {% endautoescape %}
+
+Escaping
+--------
+
+It is sometimes desirable or even necessary to have Twig ignore parts it would
+otherwise handle as variables or blocks. For example if the default syntax is
+used and you want to use ``{{`` as raw string in the template and not start a
+variable you have to use a trick.
+
+The easiest way is to output the variable delimiter (``{{``) by using a variable
+expression:
+
+.. code-block:: jinja
+
+    {{ '{{' }}
+
+For bigger sections it makes sense to mark a block
+:doc:`verbatim<tags/verbatim>`.
+
+Macros
+------
+
+.. versionadded:: 1.12
+    Support for default argument values was added in Twig 1.12.
+
+Macros are comparable with functions in regular programming languages. They
+are useful to reuse often used HTML fragments to not repeat yourself.
+
+A macro is defined via the :doc:`macro<tags/macro>` tag. Here is a small example
+(subsequently called ``forms.html``) of a macro that renders a form element:
+
+.. code-block:: jinja
+
+    {% macro input(name, value, type, size) %}
+        <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
+    {% endmacro %}
+
+Macros can be defined in any template, and need to be "imported" via the
+:doc:`import<tags/import>` tag before being used:
+
+.. code-block:: jinja
+
+    {% import "forms.html" as forms %}
+
+    <p>{{ forms.input('username') }}</p>
+
+Alternatively, you can import individual macro names from a template into the
+current namespace via the :doc:`from<tags/from>` tag and optionally alias them:
+
+.. code-block:: jinja
+
+    {% from 'forms.html' import input as input_field %}
+
+    <dl>
+        <dt>Username</dt>
+        <dd>{{ input_field('username') }}</dd>
+        <dt>Password</dt>
+        <dd>{{ input_field('password', '', 'password') }}</dd>
+    </dl>
+
+A default value can also be defined for macro arguments when not provided in a
+macro call:
+
+.. code-block:: jinja
+
+    {% macro input(name, value = "", type = "text", size = 20) %}
+        <input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" size="{{ size }}" />
+    {% endmacro %}
+
+.. _twig-expressions:
+
+Expressions
+-----------
+
+Twig allows expressions everywhere. These work very similar to regular PHP and
+even if you're not working with PHP you should feel comfortable with it.
+
+.. note::
+
+    The operator precedence is as follows, with the lowest-precedence
+    operators listed first: ``b-and``, ``b-xor``, ``b-or``, ``or``, ``and``,
+    ``==``, ``!=``, ``<``, ``>``, ``>=``, ``<=``, ``in``, ``..``, ``+``,
+    ``-``, ``~``, ``*``, ``/``, ``//``, ``%``, ``is``, ``**``, ``|``, ``[]``,
+    and ``.``:
+
+    .. code-block:: jinja
+
+        {% set greeting = 'Hello' %}
+        {% set name = 'Fabien' %}
+
+        {{ greeting ~ name|lower }}   {# Hello fabien #}
+
+        {# use parenthesis to change precedence #}
+        {{ (greeting ~ name)|lower }} {# hello fabien #}
+
+Literals
+~~~~~~~~
+
+.. versionadded:: 1.5
+    Support for hash keys as names and expressions was added in Twig 1.5.
+
+The simplest form of expressions are literals. Literals are representations
+for PHP types such as strings, numbers, and arrays. The following literals
+exist:
+
+* ``"Hello World"``: Everything between two double or single quotes is a
+  string. They are useful whenever you need a string in the template (for
+  example as arguments to function calls, filters or just to extend or include
+  a template). A string can contain a delimiter if it is preceded by a
+  backslash (``\``) -- like in ``'It\'s good'``.
+
+* ``42`` / ``42.23``: Integers and floating point numbers are created by just
+  writing the number down. If a dot is present the number is a float,
+  otherwise an integer.
+
+* ``["foo", "bar"]``: Arrays are defined by a sequence of expressions
+  separated by a comma (``,``) and wrapped with squared brackets (``[]``).
+
+* ``{"foo": "bar"}``: Hashes are defined by a list of keys and values
+  separated by a comma (``,``) and wrapped with curly braces (``{}``):
+
+  .. code-block:: jinja
+
+    {# keys as string #}
+    { 'foo': 'foo', 'bar': 'bar' }
+
+    {# keys as names (equivalent to the previous hash) -- as of Twig 1.5 #}
+    { foo: 'foo', bar: 'bar' }
+
+    {# keys as integer #}
+    { 2: 'foo', 4: 'bar' }
+
+    {# keys as expressions (the expression must be enclosed into parentheses) -- as of Twig 1.5 #}
+    { (1 + 1): 'foo', (a ~ 'b'): 'bar' }
+
+* ``true`` / ``false``: ``true`` represents the true value, ``false``
+  represents the false value.
+
+* ``null``: ``null`` represents no specific value. This is the value returned
+  when a variable does not exist. ``none`` is an alias for ``null``.
+
+Arrays and hashes can be nested:
+
+.. code-block:: jinja
+
+    {% set foo = [1, {"foo": "bar"}] %}
+
+.. tip::
+
+    Using double-quoted or single-quoted strings has no impact on performance
+    but string interpolation is only supported in double-quoted strings.
+
+Math
+~~~~
+
+Twig allows you to calculate with values. This is rarely useful in templates
+but exists for completeness' sake. The following operators are supported:
+
+* ``+``: Adds two objects together (the operands are casted to numbers). ``{{
+  1 + 1 }}`` is ``2``.
+
+* ``-``: Subtracts the second number from the first one. ``{{ 3 - 2 }}`` is
+  ``1``.
+
+* ``/``: Divides two numbers. The returned value will be a floating point
+  number. ``{{ 1 / 2 }}`` is ``{{ 0.5 }}``.
+
+* ``%``: Calculates the remainder of an integer division. ``{{ 11 % 7 }}`` is
+  ``4``.
+
+* ``//``: Divides two numbers and returns the truncated integer result. ``{{
+  20 // 7 }}`` is ``2``.
+
+* ``*``: Multiplies the left operand with the right one. ``{{ 2 * 2 }}`` would
+  return ``4``.
+
+* ``**``: Raises the left operand to the power of the right operand. ``{{ 2 **
+  3 }}`` would return ``8``.
+
+Logic
+~~~~~
+
+You can combine multiple expressions with the following operators:
+
+* ``and``: Returns true if the left and the right operands are both true.
+
+* ``or``: Returns true if the left or the right operand is true.
+
+* ``not``: Negates a statement.
+
+* ``(expr)``: Groups an expression.
+
+.. note::
+
+    Twig also support bitwise operators (``b-and``, ``b-xor``, and ``b-or``).
+
+Comparisons
+~~~~~~~~~~~
+
+The following comparison operators are supported in any expression: ``==``,
+``!=``, ``<``, ``>``, ``>=``, and ``<=``.
+
+Containment Operator
+~~~~~~~~~~~~~~~~~~~~
+
+The ``in`` operator performs containment test.
+
+It returns ``true`` if the left operand is contained in the right:
+
+.. code-block:: jinja
+
+    {# returns true #}
+
+    {{ 1 in [1, 2, 3] }}
+
+    {{ 'cd' in 'abcde' }}
+
+.. tip::
+
+    You can use this filter to perform a containment test on strings, arrays,
+    or objects implementing the ``Traversable`` interface.
+
+To perform a negative test, use the ``not in`` operator:
+
+.. code-block:: jinja
+
+    {% if 1 not in [1, 2, 3] %}
+
+    {# is equivalent to #}
+    {% if not (1 in [1, 2, 3]) %}
+
+Test Operator
+~~~~~~~~~~~~~
+
+The ``is`` operator performs tests. Tests can be used to test a variable against
+a common expression. The right operand is name of the test:
+
+.. code-block:: jinja
+
+    {# find out if a variable is odd #}
+
+    {{ name is odd }}
+
+Tests can accept arguments too:
+
+.. code-block:: jinja
+
+    {% if loop.index is divisibleby(3) %}
+
+Tests can be negated by using the ``is not`` operator:
+
+.. code-block:: jinja
+
+    {% if loop.index is not divisibleby(3) %}
+
+    {# is equivalent to #}
+    {% if not (loop.index is divisibleby(3)) %}
+
+Go to the :doc:`tests<tests/index>` page to learn more about the built-in
+tests.
+
+Other Operators
+~~~~~~~~~~~~~~~
+
+.. versionadded:: 1.12.0
+    Support for the extended ternary operator was added in Twig 1.12.0.
+
+The following operators are very useful but don't fit into any of the other
+categories:
+
+* ``..``: Creates a sequence based on the operand before and after the
+  operator (this is just syntactic sugar for the :doc:`range<functions/range>`
+  function).
+
+* ``|``: Applies a filter.
+
+* ``~``: Converts all operands into strings and concatenates them. ``{{ "Hello
+  " ~ name ~ "!" }}`` would return (assuming ``name`` is ``'John'``) ``Hello
+  John!``.
+
+* ``.``, ``[]``: Gets an attribute of an object.
+
+* ``?:``: The ternary operator:
+
+  .. code-block:: jinja
+
+      {{ foo ? 'yes' : 'no' }}
+
+      {# as of Twig 1.12.0 #}
+      {{ foo ?: 'no' }} == {{ foo ? foo : 'no' }}
+      {{ foo ? 'yes' }} == {{ foo ? 'yes' : '' }}
+
+String Interpolation
+~~~~~~~~~~~~~~~~~~~~
+
+.. versionadded:: 1.5
+    String interpolation was added in Twig 1.5.
+
+String interpolation (`#{expression}`) allows any valid expression to appear
+within a *double-quoted string*. The result of evaluating that expression is
+inserted into the string:
+
+.. code-block:: jinja
+
+    {{ "foo #{bar} baz" }}
+    {{ "foo #{1 + 2} baz" }}
+
+Whitespace Control
+------------------
+
+.. versionadded:: 1.1
+    Tag level whitespace control was added in Twig 1.1.
+
+The first newline after a template tag is removed automatically (like in PHP.)
+Whitespace is not further modified by the template engine, so each whitespace
+(spaces, tabs, newlines etc.) is returned unchanged.
+
+Use the ``spaceless`` tag to remove whitespace *between HTML tags*:
+
+.. code-block:: jinja
+
+    {% spaceless %}
+        <div>
+            <strong>foo bar</strong>
+        </div>
+    {% endspaceless %}
+
+    {# output will be <div><strong>foo bar</strong></div> #}
+
+In addition to the spaceless tag you can also control whitespace on a per tag
+level. By using the whitespace control modifier on your tags, you can trim
+leading and or trailing whitespace:
+
+.. code-block:: jinja
+
+    {% set value = 'no spaces' %}
+    {#- No leading/trailing whitespace -#}
+    {%- if true -%}
+        {{- value -}}
+    {%- endif -%}
+
+    {# output 'no spaces' #}
+
+The above sample shows the default whitespace control modifier, and how you can
+use it to remove whitespace around tags.  Trimming space will consume all whitespace
+for that side of the tag.  It is possible to use whitespace trimming on one side
+of a tag:
+
+.. code-block:: jinja
+
+    {% set value = 'no spaces' %}
+    <li>    {{- value }}    </li>
+
+    {# outputs '<li>no spaces    </li>' #}
+
+Extensions
+----------
+
+Twig can be easily extended.
+
+If you are looking for new tags, filters, or functions, have a look at the Twig official
+`extension repository`_.
+
+If you want to create your own, read the :ref:`Creating an
+Extension<creating_extensions>` chapter.
+
+.. _`Twig bundle`:                https://github.com/Anomareh/PHP-Twig.tmbundle
+.. _`Jinja syntax plugin`:        http://jinja.pocoo.org/2/documentation/integration
+.. _`Twig syntax plugin`:         http://plugins.netbeans.org/plugin/37069/php-twig
+.. _`Twig plugin`:                https://github.com/pulse00/Twig-Eclipse-Plugin
+.. _`Twig language definition`:   https://github.com/gabrielcorpse/gedit-twig-template-language
+.. _`extension repository`:       http://github.com/fabpot/Twig-extensions
+.. _`Twig syntax mode`:           https://github.com/bobthecow/Twig-HTML.mode
+.. _`other Twig syntax mode`:     https://github.com/muxx/Twig-HTML.mode
+.. _`Notepad++ Twig Highlighter`: https://github.com/Banane9/notepadplusplus-twig
+.. _`web-mode.el`:                http://web-mode.org/
diff --git a/vendor/twig/twig/doc/tests/constant.rst b/vendor/twig/twig/doc/tests/constant.rst
new file mode 100644 (file)
index 0000000..8d0724a
--- /dev/null
@@ -0,0 +1,22 @@
+``constant``
+============
+
+.. versionadded: 1.13.1
+    constant now accepts object instances as the second argument.
+
+``constant`` checks if a variable has the exact same value as a constant. You
+can use either global constants or class constants:
+
+.. code-block:: jinja
+
+    {% if post.status is constant('Post::PUBLISHED') %}
+        the status attribute is exactly the same as Post::PUBLISHED
+    {% endif %}
+
+You can test constants from object instances as well:
+
+.. code-block:: jinja
+
+    {% if post.status is constant('PUBLISHED', post) %}
+        the status attribute is exactly the same as Post::PUBLISHED
+    {% endif %}
diff --git a/vendor/twig/twig/doc/tests/defined.rst b/vendor/twig/twig/doc/tests/defined.rst
new file mode 100644 (file)
index 0000000..702ce72
--- /dev/null
@@ -0,0 +1,30 @@
+``defined``
+===========
+
+``defined`` checks if a variable is defined in the current context. This is very
+useful if you use the ``strict_variables`` option:
+
+.. code-block:: jinja
+
+    {# defined works with variable names #}
+    {% if foo is defined %}
+        ...
+    {% endif %}
+
+    {# and attributes on variables names #}
+    {% if foo.bar is defined %}
+        ...
+    {% endif %}
+
+    {% if foo['bar'] is defined %}
+        ...
+    {% endif %}
+
+When using the ``defined`` test on an expression that uses variables in some
+method calls, be sure that they are all defined first:
+
+.. code-block:: jinja
+
+    {% if var is defined and foo.method(var) is defined %}
+        ...
+    {% endif %}
diff --git a/vendor/twig/twig/doc/tests/divisibleby.rst b/vendor/twig/twig/doc/tests/divisibleby.rst
new file mode 100644 (file)
index 0000000..9b0b964
--- /dev/null
@@ -0,0 +1,10 @@
+``divisibleby``
+===============
+
+``divisibleby`` checks if a variable is divisible by a number:
+
+.. code-block:: jinja
+
+    {% if loop.index is divisibleby(3) %}
+        ...
+    {% endif %}
diff --git a/vendor/twig/twig/doc/tests/empty.rst b/vendor/twig/twig/doc/tests/empty.rst
new file mode 100644 (file)
index 0000000..e5b5599
--- /dev/null
@@ -0,0 +1,11 @@
+``empty``
+=========
+
+``empty`` checks if a variable is empty:
+
+.. code-block:: jinja
+
+    {# evaluates to true if the foo variable is null, false, an empty array, or the empty string #}
+    {% if foo is empty %}
+        ...
+    {% endif %}
diff --git a/vendor/twig/twig/doc/tests/even.rst b/vendor/twig/twig/doc/tests/even.rst
new file mode 100644 (file)
index 0000000..6ab5cc3
--- /dev/null
@@ -0,0 +1,10 @@
+``even``
+========
+
+``even`` returns ``true`` if the given number is even:
+
+.. code-block:: jinja
+
+    {{ var is even }}
+
+.. seealso:: :doc:`odd<../tests/odd>`
diff --git a/vendor/twig/twig/doc/tests/index.rst b/vendor/twig/twig/doc/tests/index.rst
new file mode 100644 (file)
index 0000000..c63208e
--- /dev/null
@@ -0,0 +1,15 @@
+Tests
+=====
+
+.. toctree::
+    :maxdepth: 1
+
+    constant
+    defined
+    divisibleby
+    empty
+    even
+    iterable
+    null
+    odd
+    sameas
diff --git a/vendor/twig/twig/doc/tests/iterable.rst b/vendor/twig/twig/doc/tests/iterable.rst
new file mode 100644 (file)
index 0000000..89a172f
--- /dev/null
@@ -0,0 +1,19 @@
+``iterable``
+============
+
+.. versionadded:: 1.7
+    The iterable test was added in Twig 1.7.
+
+``iterable`` checks if a variable is an array or a traversable object:
+
+.. code-block:: jinja
+
+    {# evaluates to true if the foo variable is iterable #}
+    {% if users is iterable %}
+        {% for user in users %}
+            Hello {{ user }}!
+        {% endfor %}
+    {% else %}
+        {# users is probably a string #}
+        Hello {{ users }}!
+    {% endif %}
diff --git a/vendor/twig/twig/doc/tests/null.rst b/vendor/twig/twig/doc/tests/null.rst
new file mode 100644 (file)
index 0000000..44eec62
--- /dev/null
@@ -0,0 +1,12 @@
+``null``
+========
+
+``null`` returns ``true`` if the variable is ``null``:
+
+.. code-block:: jinja
+
+    {{ var is null }}
+
+.. note::
+
+    ``none`` is an alias for ``null``.
diff --git a/vendor/twig/twig/doc/tests/odd.rst b/vendor/twig/twig/doc/tests/odd.rst
new file mode 100644 (file)
index 0000000..9eece77
--- /dev/null
@@ -0,0 +1,10 @@
+``odd``
+=======
+
+``odd`` returns ``true`` if the given number is odd:
+
+.. code-block:: jinja
+
+    {{ var is odd }}
+
+.. seealso:: :doc:`even<../tests/even>`
diff --git a/vendor/twig/twig/doc/tests/sameas.rst b/vendor/twig/twig/doc/tests/sameas.rst
new file mode 100644 (file)
index 0000000..efb15c3
--- /dev/null
@@ -0,0 +1,11 @@
+``sameas``
+==========
+
+``sameas`` checks if a variable points to the same memory address than another
+variable:
+
+.. code-block:: jinja
+
+    {% if foo.attribute is sameas(false) %}
+        the foo attribute really is the ``false`` PHP value
+    {% endif %}
diff --git a/vendor/twig/twig/ext/twig/.gitignore b/vendor/twig/twig/ext/twig/.gitignore
new file mode 100644 (file)
index 0000000..56ea76c
--- /dev/null
@@ -0,0 +1,30 @@
+*.sw*
+.deps
+Makefile
+Makefile.fragments
+Makefile.global
+Makefile.objects
+acinclude.m4
+aclocal.m4
+build/
+config.cache
+config.guess
+config.h
+config.h.in
+config.log
+config.nice
+config.status
+config.sub
+configure
+configure.in
+install-sh
+libtool
+ltmain.sh
+missing
+mkinstalldirs
+run-tests.php
+twig.loT
+.libs/
+modules/
+twig.la
+twig.lo
diff --git a/vendor/twig/twig/ext/twig/LICENSE b/vendor/twig/twig/ext/twig/LICENSE
new file mode 100644 (file)
index 0000000..66b8bb4
--- /dev/null
@@ -0,0 +1,22 @@
+Copyright (c) 2011, Derick Rethans <derick@derickrethans.nl>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without 
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, 
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright 
+      notice, this list of conditions and the following disclaimer in the 
+      documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/twig/twig/ext/twig/config.m4 b/vendor/twig/twig/ext/twig/config.m4
new file mode 100644 (file)
index 0000000..83486be
--- /dev/null
@@ -0,0 +1,8 @@
+dnl config.m4 for extension twig
+
+PHP_ARG_ENABLE(twig, whether to enable twig support,
+[  --enable-twig           Enable twig support])
+
+if test "$PHP_TWIG" != "no"; then
+  PHP_NEW_EXTENSION(twig, twig.c, $ext_shared)
+fi
diff --git a/vendor/twig/twig/ext/twig/config.w32 b/vendor/twig/twig/ext/twig/config.w32
new file mode 100644 (file)
index 0000000..cb287b9
--- /dev/null
@@ -0,0 +1,8 @@
+// vim:ft=javascript
+
+ARG_ENABLE("twig", "Twig support", "no");
+
+if (PHP_TWIG != "no") {
+       AC_DEFINE('HAVE_TWIG', 1);
+       EXTENSION('twig', 'twig.c');
+}
diff --git a/vendor/twig/twig/ext/twig/php_twig.h b/vendor/twig/twig/ext/twig/php_twig.h
new file mode 100644 (file)
index 0000000..1cf0ad4
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+   +----------------------------------------------------------------------+
+   | Twig Extension                                                       |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 2011 Derick Rethans                                    |
+   +----------------------------------------------------------------------+
+   | Redistribution and use in source and binary forms, with or without   |
+   | modification, are permitted provided that the conditions mentioned   |
+   | in the accompanying LICENSE file are met (BSD, revised).             |
+   +----------------------------------------------------------------------+
+   | Author: Derick Rethans <derick@derickrethans.nl>                     |
+   +----------------------------------------------------------------------+
+ */
+
+#ifndef PHP_TWIG_H
+#define PHP_TWIG_H
+
+#define PHP_TWIG_VERSION "1.13.2"
+
+#include "php.h"
+
+extern zend_module_entry twig_module_entry;
+#define phpext_twig_ptr &twig_module_entry
+
+#ifdef ZTS
+#include "TSRM.h"
+#endif
+
+PHP_FUNCTION(twig_template_get_attributes);
+
+#endif
diff --git a/vendor/twig/twig/ext/twig/twig.c b/vendor/twig/twig/ext/twig/twig.c
new file mode 100644 (file)
index 0000000..3ba9ff2
--- /dev/null
@@ -0,0 +1,1076 @@
+/*
+   +----------------------------------------------------------------------+
+   | Twig Extension                                                       |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 2011 Derick Rethans                                    |
+   +----------------------------------------------------------------------+
+   | Redistribution and use in source and binary forms, with or without   |
+   | modification, are permitted provided that the conditions mentioned   |
+   | in the accompanying LICENSE file are met (BSD, revised).             |
+   +----------------------------------------------------------------------+
+   | Author: Derick Rethans <derick@derickrethans.nl>                     |
+   +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_twig.h"
+#include "ext/standard/php_string.h"
+#include "ext/standard/php_smart_str.h"
+
+#include "Zend/zend_object_handlers.h"
+#include "Zend/zend_interfaces.h"
+#include "Zend/zend_exceptions.h"
+
+#ifndef Z_ADDREF_P
+#define Z_ADDREF_P(pz)                (pz)->refcount++
+#endif
+
+#define FREE_DTOR(z)   \
+       zval_dtor(z);           \
+       efree(z);
+
+#if PHP_VERSION_ID >= 50300
+       #define APPLY_TSRMLS_DC TSRMLS_DC
+       #define APPLY_TSRMLS_CC TSRMLS_CC
+       #define APPLY_TSRMLS_FETCH()
+#else
+       #define APPLY_TSRMLS_DC
+       #define APPLY_TSRMLS_CC
+       #define APPLY_TSRMLS_FETCH() TSRMLS_FETCH()
+#endif
+
+ZEND_BEGIN_ARG_INFO_EX(twig_template_get_attribute_args, ZEND_SEND_BY_VAL, ZEND_RETURN_VALUE, 6)
+       ZEND_ARG_INFO(0, template)
+       ZEND_ARG_INFO(0, object)
+       ZEND_ARG_INFO(0, item)
+       ZEND_ARG_INFO(0, arguments)
+       ZEND_ARG_INFO(0, type)
+       ZEND_ARG_INFO(0, isDefinedTest)
+ZEND_END_ARG_INFO()
+
+zend_function_entry twig_functions[] = {
+       PHP_FE(twig_template_get_attributes, twig_template_get_attribute_args)
+       {NULL, NULL, NULL}
+};
+
+
+zend_module_entry twig_module_entry = {
+       STANDARD_MODULE_HEADER,
+       "twig",
+       twig_functions,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       PHP_TWIG_VERSION,
+       STANDARD_MODULE_PROPERTIES
+};
+
+
+#ifdef COMPILE_DL_TWIG
+ZEND_GET_MODULE(twig)
+#endif
+
+int TWIG_ARRAY_KEY_EXISTS(zval *array, zval *key)
+{
+       zval temp;
+       int result;
+
+       if (Z_TYPE_P(array) != IS_ARRAY) {
+               return 0;
+       }
+
+       switch (Z_TYPE_P(key)) {
+               case IS_NULL:
+                       return zend_hash_exists(Z_ARRVAL_P(array), "", 1);
+
+               case IS_BOOL:
+               case IS_DOUBLE:
+                       convert_to_long(key);
+               case IS_LONG:
+                       return zend_hash_index_exists(Z_ARRVAL_P(array), Z_LVAL_P(key));
+
+               default:
+                       convert_to_string(key);
+                       return zend_symtable_exists(Z_ARRVAL_P(array), Z_STRVAL_P(key), Z_STRLEN_P(key) + 1);
+       }
+}
+
+int TWIG_INSTANCE_OF(zval *object, zend_class_entry *interface TSRMLS_DC)
+{
+       if (Z_TYPE_P(object) != IS_OBJECT) {
+               return 0;
+       }
+       return instanceof_function(Z_OBJCE_P(object), interface TSRMLS_CC);
+}
+
+int TWIG_INSTANCE_OF_USERLAND(zval *object, char *interface TSRMLS_DC)
+{
+       zend_class_entry **pce;
+       if (Z_TYPE_P(object) != IS_OBJECT) {
+               return 0;
+       }
+       if (zend_lookup_class(interface, strlen(interface), &pce TSRMLS_CC) == FAILURE) {
+               return 0;
+       }
+       return instanceof_function(Z_OBJCE_P(object), *pce TSRMLS_CC);
+}
+
+zval *TWIG_GET_ARRAYOBJECT_ELEMENT(zval *object, zval *offset TSRMLS_DC)
+{
+       zend_class_entry *ce = Z_OBJCE_P(object);
+       zval *retval;
+
+       if (Z_TYPE_P(object) == IS_OBJECT) {
+               SEPARATE_ARG_IF_REF(offset);
+               zend_call_method_with_1_params(&object, ce, NULL, "offsetget", &retval, offset);
+
+               zval_ptr_dtor(&offset);
+
+               if (!retval) {
+                       if (!EG(exception)) {
+                               zend_error(E_ERROR, "Undefined offset for object of type %s used as array", ce->name);
+                       }
+                       return NULL;
+               }
+
+               return retval;
+       }
+       return NULL;
+}
+
+int TWIG_ISSET_ARRAYOBJECT_ELEMENT(zval *object, zval *offset TSRMLS_DC)
+{
+       zend_class_entry *ce = Z_OBJCE_P(object);
+       zval *retval;
+
+       if (Z_TYPE_P(object) == IS_OBJECT) {
+               SEPARATE_ARG_IF_REF(offset);
+               zend_call_method_with_1_params(&object, ce, NULL, "offsetexists", &retval, offset);
+
+               zval_ptr_dtor(&offset);
+
+               if (!retval) {
+                       if (!EG(exception)) {
+                               zend_error(E_ERROR, "Undefined offset for object of type %s used as array", ce->name);
+                       }
+                       return 0;
+               }
+
+               return (retval && Z_TYPE_P(retval) == IS_BOOL && Z_LVAL_P(retval));
+       }
+       return 0;
+}
+
+char *TWIG_STRTOLOWER(const char *str, int str_len)
+{
+       char *item_dup;
+
+       item_dup = estrndup(str, str_len);
+       php_strtolower(item_dup, str_len);
+       return item_dup;
+}
+
+zval *TWIG_CALL_USER_FUNC_ARRAY(zval *object, char *function, zval *arguments TSRMLS_DC)
+{
+       zend_fcall_info fci;
+       zval ***args = NULL;
+       int arg_count = 0;
+       HashTable *table;
+       HashPosition pos;
+       int i = 0;
+       zval *retval_ptr;
+       zval *zfunction;
+
+       if (arguments) {
+               table = HASH_OF(arguments);
+               args = safe_emalloc(sizeof(zval **), table->nNumOfElements, 0);
+
+               zend_hash_internal_pointer_reset_ex(table, &pos);
+
+               while (zend_hash_get_current_data_ex(table, (void **)&args[i], &pos) == SUCCESS) {
+                       i++;
+                       zend_hash_move_forward_ex(table, &pos);
+               }
+               arg_count = table->nNumOfElements;
+       }
+
+       MAKE_STD_ZVAL(zfunction);
+       ZVAL_STRING(zfunction, function, 1);
+       fci.size = sizeof(fci);
+       fci.function_table = EG(function_table);
+       fci.function_name = zfunction;
+       fci.symbol_table = NULL;
+#if PHP_VERSION_ID >= 50300
+       fci.object_ptr = object;
+#else
+       fci.object_pp = &object;
+#endif
+       fci.retval_ptr_ptr = &retval_ptr;
+       fci.param_count = arg_count;
+       fci.params = args;
+       fci.no_separation = 0;
+
+       if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
+               ALLOC_INIT_ZVAL(retval_ptr);
+               ZVAL_BOOL(retval_ptr, 0);
+       }
+
+       if (args) {
+               efree(fci.params);
+       }
+       FREE_DTOR(zfunction);
+       return retval_ptr;
+}
+
+int TWIG_CALL_BOOLEAN(zval *object, char *functionName TSRMLS_DC)
+{
+       zval *ret;
+       int   res;
+
+       ret = TWIG_CALL_USER_FUNC_ARRAY(object, functionName, NULL TSRMLS_CC);
+       res = Z_LVAL_P(ret);
+       zval_ptr_dtor(&ret);
+       return res;
+}
+
+zval *TWIG_GET_STATIC_PROPERTY(zval *class, char *prop_name TSRMLS_DC)
+{
+       zval **tmp_zval;
+       zend_class_entry *ce;
+
+       if (class == NULL || Z_TYPE_P(class) != IS_OBJECT) {
+               return NULL;
+       }
+
+       ce = zend_get_class_entry(class TSRMLS_CC);
+#if PHP_VERSION_ID >= 50400
+       tmp_zval = zend_std_get_static_property(ce, prop_name, strlen(prop_name), 0, NULL TSRMLS_CC);
+#else
+       tmp_zval = zend_std_get_static_property(ce, prop_name, strlen(prop_name), 0 TSRMLS_CC);
+#endif
+       return *tmp_zval;
+}
+
+zval *TWIG_GET_ARRAY_ELEMENT_ZVAL(zval *class, zval *prop_name TSRMLS_DC)
+{
+       zval **tmp_zval;
+       char *tmp_name;
+
+       if (class == NULL || Z_TYPE_P(class) != IS_ARRAY) {
+               if (class != NULL && Z_TYPE_P(class) == IS_OBJECT && TWIG_INSTANCE_OF(class, zend_ce_arrayaccess TSRMLS_CC)) {
+                       // array access object
+                       return TWIG_GET_ARRAYOBJECT_ELEMENT(class, prop_name TSRMLS_CC);
+               }
+               return NULL;
+       }
+
+       switch(Z_TYPE_P(prop_name)) {
+               case IS_NULL:
+                       zend_hash_find(HASH_OF(class), "", 1, (void**) &tmp_zval);
+                       return *tmp_zval;
+
+               case IS_BOOL:
+               case IS_DOUBLE:
+                       convert_to_long(prop_name);
+               case IS_LONG:
+                       zend_hash_index_find(HASH_OF(class), Z_LVAL_P(prop_name), (void **) &tmp_zval);
+                       return *tmp_zval;
+
+               case IS_STRING:
+                       zend_symtable_find(HASH_OF(class), Z_STRVAL_P(prop_name), Z_STRLEN_P(prop_name) + 1, (void**) &tmp_zval);
+                       return *tmp_zval;
+       }
+
+       return NULL;
+}
+
+zval *TWIG_GET_ARRAY_ELEMENT(zval *class, char *prop_name, int prop_name_length TSRMLS_DC)
+{
+       zval **tmp_zval;
+
+       if (class == NULL/* || Z_TYPE_P(class) != IS_ARRAY*/) {
+               return NULL;
+       }
+
+       if (class != NULL && Z_TYPE_P(class) == IS_OBJECT && TWIG_INSTANCE_OF(class, zend_ce_arrayaccess TSRMLS_CC)) {
+               // array access object
+               zval *tmp_name_zval;
+               zval *tmp_ret_zval;
+
+               ALLOC_INIT_ZVAL(tmp_name_zval);
+               ZVAL_STRING(tmp_name_zval, prop_name, 1);
+               tmp_ret_zval = TWIG_GET_ARRAYOBJECT_ELEMENT(class, tmp_name_zval TSRMLS_CC);
+               FREE_DTOR(tmp_name_zval);
+               return tmp_ret_zval;
+       }
+
+       if (zend_symtable_find(HASH_OF(class), prop_name, prop_name_length+1, (void**)&tmp_zval) == SUCCESS) {
+               return *tmp_zval;
+       }
+       return NULL;
+}
+
+zval *TWIG_PROPERTY(zval *object, zval *propname TSRMLS_DC)
+{
+       zval *tmp = NULL;
+
+       if (Z_OBJ_HT_P(object)->read_property) {
+#if PHP_VERSION_ID >= 50400
+               tmp = Z_OBJ_HT_P(object)->read_property(object, propname, BP_VAR_IS, NULL TSRMLS_CC);
+#else
+               tmp = Z_OBJ_HT_P(object)->read_property(object, propname, BP_VAR_IS TSRMLS_CC);
+#endif
+               if (tmp != EG(uninitialized_zval_ptr)) {
+                       return tmp;
+               } else {
+                       return NULL;
+               }
+       }
+       return tmp;
+}
+
+int TWIG_HAS_PROPERTY(zval *object, zval *propname TSRMLS_DC)
+{
+       if (Z_OBJ_HT_P(object)->has_property) {
+#if PHP_VERSION_ID >= 50400
+               return Z_OBJ_HT_P(object)->has_property(object, propname, 0, NULL TSRMLS_CC);
+#else
+               return Z_OBJ_HT_P(object)->has_property(object, propname, 0 TSRMLS_CC);
+#endif
+       }
+       return 0;
+}
+
+int TWIG_HAS_DYNAMIC_PROPERTY(zval *object, char *prop, int prop_len TSRMLS_DC)
+{
+       if (Z_OBJ_HT_P(object)->get_properties) {
+               return zend_hash_quick_exists(
+                               Z_OBJ_HT_P(object)->get_properties(object TSRMLS_CC), // the properties hash
+                               prop,                                                 // property name
+                               prop_len + 1,                                         // property length
+                               zend_get_hash_value(prop, prop_len + 1)               // hash value
+                       );
+       }
+       return 0;
+}
+
+zval *TWIG_PROPERTY_CHAR(zval *object, char *propname TSRMLS_DC)
+{
+       zval *tmp_name_zval, *tmp;
+
+       ALLOC_INIT_ZVAL(tmp_name_zval);
+       ZVAL_STRING(tmp_name_zval, propname, 1);
+       tmp = TWIG_PROPERTY(object, tmp_name_zval TSRMLS_CC);
+       FREE_DTOR(tmp_name_zval);
+       return tmp;
+}
+
+int TWIG_CALL_B_0(zval *object, char *method)
+{
+       return 0;
+}
+
+zval *TWIG_CALL_S(zval *object, char *method, char *arg0 TSRMLS_DC)
+{
+       zend_fcall_info fci;
+       zval **args[1];
+       zval *argument;
+       zval *zfunction;
+       zval *retval_ptr;
+
+       MAKE_STD_ZVAL(argument);
+       ZVAL_STRING(argument, arg0, 1);
+       args[0] = &argument;
+
+       MAKE_STD_ZVAL(zfunction);
+       ZVAL_STRING(zfunction, method, 1);
+       fci.size = sizeof(fci);
+       fci.function_table = EG(function_table);
+       fci.function_name = zfunction;
+       fci.symbol_table = NULL;
+#if PHP_VERSION_ID >= 50300
+       fci.object_ptr = object;
+#else
+       fci.object_pp = &object;
+#endif
+       fci.retval_ptr_ptr = &retval_ptr;
+       fci.param_count = 1;
+       fci.params = args;
+       fci.no_separation = 0;
+
+       if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
+               FREE_DTOR(zfunction);
+               zval_ptr_dtor(&argument);
+               return 0;
+       }
+       FREE_DTOR(zfunction);
+       zval_ptr_dtor(&argument);
+       return retval_ptr;
+}
+
+int TWIG_CALL_SB(zval *object, char *method, char *arg0 TSRMLS_DC)
+{
+       zval *retval_ptr;
+       int success;
+
+       retval_ptr = TWIG_CALL_S(object, method, arg0 TSRMLS_CC);
+       success = (retval_ptr && (Z_TYPE_P(retval_ptr) == IS_BOOL) && Z_LVAL_P(retval_ptr));
+
+       if (retval_ptr) {
+               zval_ptr_dtor(&retval_ptr);
+       }
+
+       return success;
+}
+
+int TWIG_CALL_Z(zval *object, char *method, zval *arg1 TSRMLS_DC)
+{
+       zend_fcall_info fci;
+       zval **args[1];
+       zval *zfunction;
+       zval *retval_ptr;
+       int   success;
+
+       args[0] = &arg1;
+
+       MAKE_STD_ZVAL(zfunction);
+       ZVAL_STRING(zfunction, method, 1);
+       fci.size = sizeof(fci);
+       fci.function_table = EG(function_table);
+       fci.function_name = zfunction;
+       fci.symbol_table = NULL;
+#if PHP_VERSION_ID >= 50300
+       fci.object_ptr = object;
+#else
+       fci.object_pp = &object;
+#endif
+       fci.retval_ptr_ptr = &retval_ptr;
+       fci.param_count = 1;
+       fci.params = args;
+       fci.no_separation = 0;
+
+       if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
+               FREE_DTOR(zfunction);
+               if (retval_ptr) {
+                       zval_ptr_dtor(&retval_ptr);
+               }
+               return 0;
+       }
+
+       FREE_DTOR(zfunction);
+
+       success = (retval_ptr && (Z_TYPE_P(retval_ptr) == IS_BOOL) && Z_LVAL_P(retval_ptr));
+       if (retval_ptr) {
+               zval_ptr_dtor(&retval_ptr);
+       }
+
+       return success;
+}
+
+int TWIG_CALL_ZZ(zval *object, char *method, zval *arg1, zval *arg2 TSRMLS_DC)
+{
+       zend_fcall_info fci;
+       zval **args[2];
+       zval *zfunction;
+       zval *retval_ptr;
+       int   success;
+
+       args[0] = &arg1;
+       args[1] = &arg2;
+
+       MAKE_STD_ZVAL(zfunction);
+       ZVAL_STRING(zfunction, method, 1);
+       fci.size = sizeof(fci);
+       fci.function_table = EG(function_table);
+       fci.function_name = zfunction;
+       fci.symbol_table = NULL;
+#if PHP_VERSION_ID >= 50300
+       fci.object_ptr = object;
+#else
+       fci.object_pp = &object;
+#endif
+       fci.retval_ptr_ptr = &retval_ptr;
+       fci.param_count = 2;
+       fci.params = args;
+       fci.no_separation = 0;
+
+       if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
+               FREE_DTOR(zfunction);
+               return 0;
+       }
+
+       FREE_DTOR(zfunction);
+
+       success = (retval_ptr && (Z_TYPE_P(retval_ptr) == IS_BOOL) && Z_LVAL_P(retval_ptr));
+       if (retval_ptr) {
+               zval_ptr_dtor(&retval_ptr);
+       }
+
+       return success;
+}
+
+#ifndef Z_SET_REFCOUNT_P
+# define Z_SET_REFCOUNT_P(pz, rc)  pz->refcount = rc
+# define Z_UNSET_ISREF_P(pz) pz->is_ref = 0
+#endif
+
+void TWIG_NEW(zval *object, char *class, zval *arg0, zval *arg1 TSRMLS_DC)
+{
+       zend_class_entry **pce;
+
+       if (zend_lookup_class(class, strlen(class), &pce TSRMLS_CC) == FAILURE) {
+               return;
+       }
+
+       Z_TYPE_P(object) = IS_OBJECT;
+       object_init_ex(object, *pce);
+       Z_SET_REFCOUNT_P(object, 1);
+       Z_UNSET_ISREF_P(object);
+
+       TWIG_CALL_ZZ(object, "__construct", arg0, arg1 TSRMLS_CC);
+}
+
+static int twig_add_array_key_to_string(void *pDest APPLY_TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
+{
+       smart_str *buf;
+       char *joiner;
+       APPLY_TSRMLS_FETCH();
+
+       buf = va_arg(args, smart_str*);
+       joiner = va_arg(args, char*);
+
+       if (buf->len != 0) {
+               smart_str_appends(buf, joiner);
+       }
+
+       if (hash_key->nKeyLength == 0) {
+               smart_str_append_long(buf, (long) hash_key->h);
+       } else {
+               char *key, *tmp_str;
+               int key_len, tmp_len;
+               key = php_addcslashes(hash_key->arKey, hash_key->nKeyLength - 1, &key_len, 0, "'\\", 2 TSRMLS_CC);
+               tmp_str = php_str_to_str_ex(key, key_len, "\0", 1, "' . \"\\0\" . '", 12, &tmp_len, 0, NULL);
+
+               smart_str_appendl(buf, tmp_str, tmp_len);
+               efree(key);
+               efree(tmp_str);
+       }
+
+       return 0;
+}
+
+char *TWIG_IMPLODE_ARRAY_KEYS(char *joiner, zval *array TSRMLS_DC)
+{
+       smart_str collector = { 0, 0, 0 };
+
+       smart_str_appendl(&collector, "", 0);
+       zend_hash_apply_with_arguments(HASH_OF(array) APPLY_TSRMLS_CC, twig_add_array_key_to_string, 2, &collector, joiner);
+       smart_str_0(&collector);
+
+       return collector.c;
+}
+
+static void TWIG_THROW_EXCEPTION(char *exception_name TSRMLS_DC, char *message, ...)
+{
+       char *buffer;
+       va_list args;
+       zend_class_entry **pce;
+
+       if (zend_lookup_class(exception_name, strlen(exception_name), &pce TSRMLS_CC) == FAILURE) {
+               return;
+       }
+
+       va_start(args, message);
+       vspprintf(&buffer, 0, message, args);
+       va_end(args);
+
+       zend_throw_exception_ex(*pce, 0 TSRMLS_CC, buffer);
+       efree(buffer);
+}
+
+static void TWIG_RUNTIME_ERROR(zval *template TSRMLS_DC, char *message, ...)
+{
+       char *buffer;
+       va_list args;
+       zend_class_entry **pce;
+       zval *ex;
+       zval *constructor;
+       zval *zmessage;
+       zval *lineno;
+       zval *filename_func;
+       zval *filename;
+       zval *constructor_args[3];
+       zval *constructor_retval;
+
+       if (zend_lookup_class("Twig_Error_Runtime", strlen("Twig_Error_Runtime"), &pce TSRMLS_CC) == FAILURE) {
+               return;
+       }
+
+       va_start(args, message);
+       vspprintf(&buffer, 0, message, args);
+       va_end(args);
+
+       MAKE_STD_ZVAL(ex);
+       object_init_ex(ex, *pce);
+
+       // Call Twig_Error constructor
+       MAKE_STD_ZVAL(constructor);
+       MAKE_STD_ZVAL(zmessage);
+       MAKE_STD_ZVAL(lineno);
+       MAKE_STD_ZVAL(filename);
+       MAKE_STD_ZVAL(filename_func);
+       MAKE_STD_ZVAL(constructor_retval);
+
+       ZVAL_STRINGL(constructor, "__construct", sizeof("__construct")-1, 1);
+       ZVAL_STRING(zmessage, buffer, 1);
+       ZVAL_LONG(lineno, -1);
+
+       // Get template filename
+       ZVAL_STRINGL(filename_func, "getTemplateName", sizeof("getTemplateName")-1, 1);
+       call_user_function(EG(function_table), &template, filename_func, filename, 0, 0 TSRMLS_CC);
+
+       constructor_args[0] = zmessage;
+       constructor_args[1] = lineno;
+       constructor_args[2] = filename;
+       call_user_function(EG(function_table), &ex, constructor, constructor_retval, 3, constructor_args TSRMLS_CC);
+
+       zval_ptr_dtor(&constructor_retval);
+       zval_ptr_dtor(&zmessage);
+       zval_ptr_dtor(&lineno);
+       zval_ptr_dtor(&filename);
+       FREE_DTOR(constructor);
+       FREE_DTOR(filename_func);
+       efree(buffer);
+
+       zend_throw_exception_object(ex TSRMLS_CC);
+}
+
+static char *TWIG_GET_CLASS_NAME(zval *object TSRMLS_DC)
+{
+       char *class_name;
+       zend_uint class_name_len;
+
+       if (Z_TYPE_P(object) != IS_OBJECT) {
+               return "";
+       }
+#if PHP_API_VERSION >= 20100412
+       zend_get_object_classname(object, (const char **) &class_name, &class_name_len TSRMLS_CC);
+#else
+       zend_get_object_classname(object, &class_name, &class_name_len TSRMLS_CC);
+#endif
+       return class_name;
+}
+
+static int twig_add_method_to_class(void *pDest APPLY_TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
+{
+       zval *retval;
+       char *item;
+       size_t item_len;
+       zend_function *mptr = (zend_function *) pDest;
+       APPLY_TSRMLS_FETCH();
+
+       if (!(mptr->common.fn_flags & ZEND_ACC_PUBLIC)) {
+               return 0;
+       }
+
+       retval = va_arg(args, zval*);
+
+       item_len = strlen(mptr->common.function_name);
+       item = estrndup(mptr->common.function_name, item_len);
+       php_strtolower(item, item_len);
+
+       add_assoc_stringl_ex(retval, item, item_len+1, item, item_len, 0);
+
+       return 0;
+}
+
+static int twig_add_property_to_class(void *pDest APPLY_TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
+{
+       zend_class_entry *ce;
+       zval *retval;
+       char *class_name, *prop_name;
+       zend_property_info *pptr = (zend_property_info *) pDest;
+       APPLY_TSRMLS_FETCH();
+
+       if (!(pptr->flags & ZEND_ACC_PUBLIC)) {
+               return 0;
+       }
+
+       ce = *va_arg(args, zend_class_entry**);
+       retval = va_arg(args, zval*);
+
+#if PHP_API_VERSION >= 20100412
+       zend_unmangle_property_name(pptr->name, pptr->name_length, (const char **) &class_name, (const char **) &prop_name);
+#else
+       zend_unmangle_property_name(pptr->name, pptr->name_length, &class_name, &prop_name);
+#endif
+
+       add_assoc_string(retval, prop_name, prop_name, 1);
+
+       return 0;
+}
+
+static void twig_add_class_to_cache(zval *cache, zval *object, char *class_name TSRMLS_DC)
+{
+       zval *class_info, *class_methods, *class_properties;
+       zend_class_entry *class_ce;
+
+       class_ce = zend_get_class_entry(object TSRMLS_CC);
+
+       ALLOC_INIT_ZVAL(class_info);
+       ALLOC_INIT_ZVAL(class_methods);
+       ALLOC_INIT_ZVAL(class_properties);
+       array_init(class_info);
+       array_init(class_methods);
+       array_init(class_properties);
+       // add all methods to self::cache[$class]['methods']
+       zend_hash_apply_with_arguments(&class_ce->function_table APPLY_TSRMLS_CC, twig_add_method_to_class, 1, class_methods);
+       zend_hash_apply_with_arguments(&class_ce->properties_info APPLY_TSRMLS_CC, twig_add_property_to_class, 2, &class_ce, class_properties);
+
+       add_assoc_zval(class_info, "methods", class_methods);
+       add_assoc_zval(class_info, "properties", class_properties);
+       add_assoc_zval(cache, class_name, class_info);
+}
+
+/* {{{ proto mixed twig_template_get_attributes(TwigTemplate template, mixed object, mixed item, array arguments, string type, boolean isDefinedTest, boolean ignoreStrictCheck)
+   A C implementation of TwigTemplate::getAttribute() */
+PHP_FUNCTION(twig_template_get_attributes)
+{
+       zval *template;
+       zval *object;
+       char *item;
+       int  item_len;
+       zval *zitem, ztmpitem;
+       zval *arguments = NULL;
+       zval *ret = NULL;
+       char *type = NULL;
+       int   type_len = 0;
+       zend_bool isDefinedTest = 0;
+       zend_bool ignoreStrictCheck = 0;
+       int free_ret = 0;
+       zval *tmp_self_cache;
+
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ozz|asbb", &template, &object, &zitem, &arguments, &type, &type_len, &isDefinedTest, &ignoreStrictCheck) == FAILURE) {
+               return;
+       }
+
+       // convert the item to a string
+       ztmpitem = *zitem;
+       zval_copy_ctor(&ztmpitem);
+       convert_to_string(&ztmpitem);
+       item_len = Z_STRLEN(ztmpitem);
+       item = estrndup(Z_STRVAL(ztmpitem), item_len);
+       zval_dtor(&ztmpitem);
+
+       if (!type) {
+               type = "any";
+       }
+
+/*
+       // array
+       if (Twig_TemplateInterface::METHOD_CALL !== $type) {
+               $arrayItem = is_bool($item) || is_float($item) ? (int) $item : $item;
+
+               if ((is_array($object) && array_key_exists($arrayItem, $object))
+                       || ($object instanceof ArrayAccess && isset($object[$arrayItem]))
+               ) {
+                       if ($isDefinedTest) {
+                               return true;
+                       }
+
+                       return $object[$arrayItem];
+               }
+*/
+
+
+       if (strcmp("method", type) != 0) {
+               if ((TWIG_ARRAY_KEY_EXISTS(object, zitem))
+                       || (TWIG_INSTANCE_OF(object, zend_ce_arrayaccess TSRMLS_CC) && TWIG_ISSET_ARRAYOBJECT_ELEMENT(object, zitem TSRMLS_CC))
+               ) {
+
+                       if (isDefinedTest) {
+                               RETURN_TRUE;
+                       }
+
+                       ret = TWIG_GET_ARRAY_ELEMENT_ZVAL(object, zitem TSRMLS_CC);
+
+                       if (!ret) {
+                               ret = &EG(uninitialized_zval);
+                       }
+                       RETVAL_ZVAL(ret, 1, 0);
+                       if (free_ret) {
+                               zval_ptr_dtor(&ret);
+                       }
+                       return;
+               }
+/*
+               if (Twig_TemplateInterface::ARRAY_CALL === $type) {
+                       if ($isDefinedTest) {
+                               return false;
+                       }
+                       if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
+                               return null;
+                       }
+*/
+               if (strcmp("array", type) == 0 || Z_TYPE_P(object) != IS_OBJECT) {
+                       if (isDefinedTest) {
+                               RETURN_FALSE;
+                       }
+                       if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
+                               return;
+                       }
+/*
+                       if (is_object($object)) {
+                               throw new Twig_Error_Runtime(sprintf('Key "%s" in object (with ArrayAccess) of type "%s" does not exist', $arrayItem, get_class($object)), -1, $this->getTemplateName());
+                       } elseif (is_array($object)) {
+                               throw new Twig_Error_Runtime(sprintf('Key "%s" for array with keys "%s" does not exist', $arrayItem, implode(', ', array_keys($object))), -1, $this->getTemplateName());
+                       } elseif (Twig_TemplateInterface::ARRAY_CALL === $type) {
+                               throw new Twig_Error_Runtime(sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
+                       } else {
+                               throw new Twig_Error_Runtime(sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
+                       }
+               }
+       }
+*/
+                       if (Z_TYPE_P(object) == IS_OBJECT) {
+                               TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Key \"%s\" in object (with ArrayAccess) of type \"%s\" does not exist", item, TWIG_GET_CLASS_NAME(object TSRMLS_CC));
+                       } else if (Z_TYPE_P(object) == IS_ARRAY) {
+                               TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Key \"%s\" for array with keys \"%s\" does not exist", item, TWIG_IMPLODE_ARRAY_KEYS(", ", object TSRMLS_CC));
+                       } else {
+                               char *type_name = zend_zval_type_name(object);
+                               Z_ADDREF_P(object);
+                               convert_to_string(object);
+                               TWIG_RUNTIME_ERROR(template TSRMLS_CC,
+                                       (strcmp("array", type) == 0)
+                                               ? "Impossible to access a key (\"%s\") on a %s variable (\"%s\")"
+                                               : "Impossible to access an attribute (\"%s\") on a %s variable (\"%s\")",
+                                       item, type_name, Z_STRVAL_P(object));
+                               zval_ptr_dtor(&object);
+                       }
+                       return;
+               }
+       }
+
+/*
+       if (!is_object($object)) {
+               if ($isDefinedTest) {
+                       return false;
+               }
+*/
+
+       if (Z_TYPE_P(object) != IS_OBJECT) {
+               if (isDefinedTest) {
+                       RETURN_FALSE;
+               }
+/*
+               if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
+                       return null;
+               }
+               throw new Twig_Error_Runtime(sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
+       }
+*/
+               if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
+                       return;
+               }
+
+               char *type_name = zend_zval_type_name(object);
+               Z_ADDREF_P(object);
+               convert_to_string_ex(&object);
+
+               TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to invoke a method (\"%s\") on a %s variable (\"%s\")", item, type_name, Z_STRVAL_P(object));
+
+               zval_ptr_dtor(&object);
+
+               return;
+       }
+/*
+       $class = get_class($object);
+*/
+       char *class_name = NULL;
+       zval *tmp_class;
+
+       class_name = TWIG_GET_CLASS_NAME(object TSRMLS_CC);
+       tmp_self_cache = TWIG_GET_STATIC_PROPERTY(template, "cache" TSRMLS_CC);
+       tmp_class = TWIG_GET_ARRAY_ELEMENT(tmp_self_cache, class_name, strlen(class_name) TSRMLS_CC);
+
+       if (!tmp_class) {
+               twig_add_class_to_cache(tmp_self_cache, object, class_name TSRMLS_CC);
+               tmp_class = TWIG_GET_ARRAY_ELEMENT(tmp_self_cache, class_name, strlen(class_name) TSRMLS_CC);
+       }
+       efree(class_name);
+
+/*
+       // object property
+       if (Twig_TemplateInterface::METHOD_CALL !== $type) {
+               if (isset($object->$item) || array_key_exists((string) $item, $object)) {
+                       if ($isDefinedTest) {
+                               return true;
+                       }
+
+                       if ($this->env->hasExtension('sandbox')) {
+                               $this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item);
+                       }
+
+                       return $object->$item;
+               }
+       }
+*/
+       if (strcmp("method", type) != 0) {
+               zval *tmp_properties, *tmp_item;
+
+               tmp_properties = TWIG_GET_ARRAY_ELEMENT(tmp_class, "properties", strlen("properties") TSRMLS_CC);
+               tmp_item = TWIG_GET_ARRAY_ELEMENT(tmp_properties, item, item_len TSRMLS_CC);
+
+               if (tmp_item || TWIG_HAS_PROPERTY(object, zitem TSRMLS_CC) || TWIG_HAS_DYNAMIC_PROPERTY(object, item, item_len TSRMLS_CC)) {
+                       if (isDefinedTest) {
+                               RETURN_TRUE;
+                       }
+                       if (TWIG_CALL_SB(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "hasExtension", "sandbox" TSRMLS_CC)) {
+                               TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "sandbox" TSRMLS_CC), "checkPropertyAllowed", object, zitem TSRMLS_CC);
+                       }
+                       if (EG(exception)) {
+                               return;
+                       }
+
+                       ret = TWIG_PROPERTY(object, zitem TSRMLS_CC);
+                       RETURN_ZVAL(ret, 1, 0);
+               }
+       }
+/*
+       // object method
+       if (!isset(self::$cache[$class]['methods'])) {
+               self::$cache[$class]['methods'] = array_change_key_case(array_flip(get_class_methods($object)));
+       }
+
+       $lcItem = strtolower($item);
+       if (isset(self::$cache[$class]['methods'][$lcItem])) {
+               $method = (string) $item;
+       } elseif (isset(self::$cache[$class]['methods']['get'.$lcItem])) {
+               $method = 'get'.$item;
+       } elseif (isset(self::$cache[$class]['methods']['is'.$lcItem])) {
+               $method = 'is'.$item;
+       } elseif (isset(self::$cache[$class]['methods']['__call'])) {
+               $method = (string) $item;
+*/
+       {
+               char *lcItem = TWIG_STRTOLOWER(item, item_len);
+               int   lcItem_length;
+               char *method = NULL;
+               char *tmp_method_name_get;
+               char *tmp_method_name_is;
+               zval *tmp_methods;
+
+               lcItem_length = strlen(lcItem);
+               tmp_method_name_get = emalloc(4 + lcItem_length);
+               tmp_method_name_is  = emalloc(3 + lcItem_length);
+
+               sprintf(tmp_method_name_get, "get%s", lcItem);
+               sprintf(tmp_method_name_is, "is%s", lcItem);
+
+               tmp_methods = TWIG_GET_ARRAY_ELEMENT(tmp_class, "methods", strlen("methods") TSRMLS_CC);
+
+               if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, lcItem, lcItem_length TSRMLS_CC)) {
+                       method = item;
+               } else if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, tmp_method_name_get, lcItem_length + 3 TSRMLS_CC)) {
+                       method = tmp_method_name_get;
+               } else if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, tmp_method_name_is, lcItem_length + 2 TSRMLS_CC)) {
+                       method = tmp_method_name_is;
+               } else if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, "__call", 6 TSRMLS_CC)) {
+                       method = item;
+/*
+       } else {
+               if ($isDefinedTest) {
+                       return false;
+               }
+
+               if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
+                       return null;
+               }
+
+               throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist', $item, get_class($object)), -1, $this->getTemplateName());
+       }
+
+       if ($isDefinedTest) {
+               return true;
+       }
+*/
+               } else {
+                       efree(tmp_method_name_get);
+                       efree(tmp_method_name_is);
+                       efree(lcItem);
+
+                       if (isDefinedTest) {
+                               RETURN_FALSE;
+                       }
+                       if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
+                               return;
+                       }
+                       TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Method \"%s\" for object \"%s\" does not exist", item, TWIG_GET_CLASS_NAME(object TSRMLS_CC));
+                       return;
+               }
+
+               if (isDefinedTest) {
+                       efree(tmp_method_name_get);
+                       efree(tmp_method_name_is);
+                       efree(lcItem);
+                       RETURN_TRUE;
+               }
+/*
+       if ($this->env->hasExtension('sandbox')) {
+               $this->env->getExtension('sandbox')->checkMethodAllowed($object, $method);
+       }
+*/
+               if (TWIG_CALL_SB(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "hasExtension", "sandbox" TSRMLS_CC)) {
+                       TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "sandbox" TSRMLS_CC), "checkMethodAllowed", object, zitem TSRMLS_CC);
+               }
+               if (EG(exception)) {
+                       efree(tmp_method_name_get);
+                       efree(tmp_method_name_is);
+                       efree(lcItem);
+                       return;
+               }
+/*
+       $ret = call_user_func_array(array($object, $method), $arguments);
+*/
+               ret = TWIG_CALL_USER_FUNC_ARRAY(object, method, arguments TSRMLS_CC);
+               free_ret = 1;
+               efree(tmp_method_name_get);
+               efree(tmp_method_name_is);
+               efree(lcItem);
+       }
+/*
+       // useful when calling a template method from a template
+       // this is not supported but unfortunately heavily used in the Symfony profiler
+       if ($object instanceof Twig_TemplateInterface) {
+               return $ret === '' ? '' : new Twig_Markup($ret, $this->env->getCharset());
+       }
+
+       return $ret;
+*/
+       // ret can be null, if e.g. the called method throws an exception
+       if (ret) {
+               if (TWIG_INSTANCE_OF_USERLAND(object, "Twig_TemplateInterface" TSRMLS_CC)) {
+                       if (Z_STRLEN_P(ret) != 0) {
+                               zval *charset = TWIG_CALL_USER_FUNC_ARRAY(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getCharset", NULL TSRMLS_CC);
+                               TWIG_NEW(return_value, "Twig_Markup", ret, charset TSRMLS_CC);
+                               zval_ptr_dtor(&charset);
+                               if (ret) {
+                                       zval_ptr_dtor(&ret);
+                               }
+                               return;
+                       }
+               }
+
+               RETVAL_ZVAL(ret, 1, 0);
+               if (free_ret) {
+                       zval_ptr_dtor(&ret);
+               }
+       }
+}
similarity index 99%
rename from inc/3rdparty/Twig/Environment.php
rename to vendor/twig/twig/lib/Twig/Environment.php
index 3afa73d639d22719857d28b1c170d7ba816d7dbb..6d4c5c57473ca0eb419d6a69f0dfb8f63267929e 100644 (file)
@@ -16,7 +16,7 @@
  */
 class Twig_Environment
 {
-    const VERSION = '1.13.1';
+    const VERSION = '1.13.2';
 
     protected $charset;
     protected $loader;
@@ -728,7 +728,7 @@ class Twig_Environment
     public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
     {
         if ($this->extensionInitialized) {
-            throw new LogicException('Unable to add a node visitor as extensions have already been initialized.', $extension->getName());
+            throw new LogicException('Unable to add a node visitor as extensions have already been initialized.');
         }
 
         $this->staging->addNodeVisitor($visitor);
similarity index 94%
rename from inc/3rdparty/Twig/Error.php
rename to vendor/twig/twig/lib/Twig/Error.php
index 72d91a98bb1aa9cf9b9d9bd075396651824452b6..61a4cfa06bc6606895b154bb5023a9f894be2a58 100644 (file)
@@ -186,6 +186,7 @@ class Twig_Error extends Exception
     protected function guessTemplateInfo()
     {
         $template = null;
+        $templateClass = null;
 
         if (version_compare(phpversion(), '5.3.6', '>=')) {
             $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT);
@@ -195,8 +196,11 @@ class Twig_Error extends Exception
 
         foreach ($backtrace as $trace) {
             if (isset($trace['object']) && $trace['object'] instanceof Twig_Template && 'Twig_Template' !== get_class($trace['object'])) {
-                if (null === $this->filename || $this->filename == $trace['object']->getTemplateName()) {
+                $currentClass = get_class($trace['object']);
+                $isEmbedContainer = 0 === strpos($templateClass, $currentClass);
+                if (null === $this->filename || ($this->filename == $trace['object']->getTemplateName() && !$isEmbedContainer)) {
                     $template = $trace['object'];
+                    $templateClass = get_class($trace['object']);
                 }
             }
         }
similarity index 92%
rename from inc/3rdparty/Twig/Loader/Filesystem.php
rename to vendor/twig/twig/lib/Twig/Loader/Filesystem.php
index f9211cbdfbbd5bbe76a678fbff0cc76059e070b6..d47781ab8650a2cd9b5d6eba421d0a43e1862162 100644 (file)
@@ -16,6 +16,9 @@
  */
 class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
 {
+    /** Identifier of the main namespace. */
+    const MAIN_NAMESPACE = '__main__';
+
     protected $paths;
     protected $cache;
 
@@ -38,7 +41,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
      *
      * @return array The array of paths where to look for templates
      */
-    public function getPaths($namespace = '__main__')
+    public function getPaths($namespace = self::MAIN_NAMESPACE)
     {
         return isset($this->paths[$namespace]) ? $this->paths[$namespace] : array();
     }
@@ -46,7 +49,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
     /**
      * Returns the path namespaces.
      *
-     * The "__main__" namespace is always defined.
+     * The main namespace is always defined.
      *
      * @return array The array of defined namespaces
      */
@@ -61,7 +64,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
      * @param string|array $paths     A path or an array of paths where to look for templates
      * @param string       $namespace A path namespace
      */
-    public function setPaths($paths, $namespace = '__main__')
+    public function setPaths($paths, $namespace = self::MAIN_NAMESPACE)
     {
         if (!is_array($paths)) {
             $paths = array($paths);
@@ -81,7 +84,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
      *
      * @throws Twig_Error_Loader
      */
-    public function addPath($path, $namespace = '__main__')
+    public function addPath($path, $namespace = self::MAIN_NAMESPACE)
     {
         // invalidate the cache
         $this->cache = array();
@@ -101,7 +104,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
      *
      * @throws Twig_Error_Loader
      */
-    public function prependPath($path, $namespace = '__main__')
+    public function prependPath($path, $namespace = self::MAIN_NAMESPACE)
     {
         // invalidate the cache
         $this->cache = array();
@@ -175,7 +178,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
 
         $this->validateName($name);
 
-        $namespace = '__main__';
+        $namespace = self::MAIN_NAMESPACE;
         if (isset($name[0]) && '@' == $name[0]) {
             if (false === $pos = strpos($name, '/')) {
                 throw new Twig_Error_Loader(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name));
similarity index 95%
rename from inc/3rdparty/Twig/Node/Expression/Call.php
rename to vendor/twig/twig/lib/Twig/Node/Expression/Call.php
index 87b62deb09e69482e6f10aab42a087ec957c3311..dba9b0e6272b8f0bdc0079fd6d68c582b79f52fd 100644 (file)
@@ -146,7 +146,7 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
 
             if (array_key_exists($name, $parameters)) {
                 if (array_key_exists($pos, $parameters)) {
-                    throw new Twig_Error_Syntax(sprintf('Arguments "%s" is defined twice for %s "%s".', $name, $this->getAttribute('type'), $this->getAttribute('name')));
+                    throw new Twig_Error_Syntax(sprintf('Argument "%s" is defined twice for %s "%s".', $name, $this->getAttribute('type'), $this->getAttribute('name')));
                 }
 
                 $arguments[] = $parameters[$name];
@@ -164,8 +164,8 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
             }
         }
 
-        foreach (array_keys($parameters) as $name) {
-            throw new Twig_Error_Syntax(sprintf('Unknown argument "%s" for %s "%s".', $name, $this->getAttribute('type'), $this->getAttribute('name')));
+        if (!empty($parameters)) {
+            throw new Twig_Error_Syntax(sprintf('Unknown argument%s "%s" for %s "%s".', count($parameters) > 1 ? 's' : '' , implode('", "', array_keys($parameters)), $this->getAttribute('type'), $this->getAttribute('name')));
         }
 
         return $arguments;
diff --git a/vendor/twig/twig/phpunit.xml.dist b/vendor/twig/twig/phpunit.xml.dist
new file mode 100644 (file)
index 0000000..6c5046f
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         processIsolation="false"
+         stopOnFailure="false"
+         syntaxCheck="false"
+         bootstrap="test/bootstrap.php"
+>
+  <testsuites>
+    <testsuite name="Twig Test Suite">
+      <directory>./test/Twig/</directory>
+    </testsuite>
+  </testsuites>
+
+  <filter>
+    <whitelist>
+      <directory suffix=".php">./lib/Twig/</directory>
+    </whitelist>
+  </filter>
+</phpunit>
diff --git a/vendor/twig/twig/test/Twig/Tests/AutoloaderTest.php b/vendor/twig/twig/test/Twig/Tests/AutoloaderTest.php
new file mode 100644 (file)
index 0000000..c8b7999
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_AutoloaderTest extends PHPUnit_Framework_TestCase
+{
+    public function testAutoload()
+    {
+        $this->assertFalse(class_exists('FooBarFoo'), '->autoload() does not try to load classes that does not begin with Twig');
+
+        $autoloader = new Twig_Autoloader();
+        $this->assertNull($autoloader->autoload('Foo'), '->autoload() returns false if it is not able to load a class');
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/CompilerTest.php b/vendor/twig/twig/test/Twig/Tests/CompilerTest.php
new file mode 100644 (file)
index 0000000..ebe79ae
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_CompilerTest extends PHPUnit_Framework_TestCase
+{
+    public function testReprNumericValueWithLocale()
+    {
+        $compiler = new Twig_Compiler(new Twig_Environment());
+
+        $locale = setlocale(LC_NUMERIC, 0);
+        if (false === $locale) {
+            $this->markTestSkipped('Your platform does not support locales.');
+        }
+
+        $required_locales = array('fr_FR.UTF-8', 'fr_FR.UTF8', 'fr_FR.utf-8', 'fr_FR.utf8', 'French_France.1252');
+        if (false === setlocale(LC_ALL, $required_locales)) {
+            $this->markTestSkipped('Could not set any of required locales: ' . implode(", ", $required_locales));
+        }
+
+        $this->assertEquals('1.2', $compiler->repr(1.2)->getSource());
+        $this->assertContains('fr', strtolower(setlocale(LC_NUMERIC, 0)));
+
+        setlocale(LC_ALL, $locale);
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/EnvironmentTest.php b/vendor/twig/twig/test/Twig/Tests/EnvironmentTest.php
new file mode 100644 (file)
index 0000000..22461b5
--- /dev/null
@@ -0,0 +1,288 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
+{
+    /**
+     * @expectedException        LogicException
+     * @expectedExceptionMessage You must set a loader first.
+     */
+    public function testRenderNoLoader()
+    {
+        $env = new Twig_Environment();
+        $env->render('test');
+    }
+
+    public function testAutoescapeOption()
+    {
+        $loader = new Twig_Loader_Array(array(
+            'html' => '{{ foo }} {{ foo }}',
+            'js'   => '{{ bar }} {{ bar }}',
+        ));
+
+        $twig = new Twig_Environment($loader, array(
+            'debug'      => true,
+            'cache'      => false,
+            'autoescape' => array($this, 'escapingStrategyCallback'),
+        ));
+
+        $this->assertEquals('foo&lt;br/ &gt; foo&lt;br/ &gt;', $twig->render('html', array('foo' => 'foo<br/ >')));
+        $this->assertEquals('foo\x3Cbr\x2F\x20\x3E foo\x3Cbr\x2F\x20\x3E', $twig->render('js', array('bar' => 'foo<br/ >')));
+    }
+
+    public function escapingStrategyCallback($filename)
+    {
+        return $filename;
+    }
+
+    public function testGlobals()
+    {
+        // globals can be added after calling getGlobals
+        $twig = new Twig_Environment(new Twig_Loader_String());
+        $twig->addGlobal('foo', 'foo');
+        $globals = $twig->getGlobals();
+        $twig->addGlobal('foo', 'bar');
+        $globals = $twig->getGlobals();
+        $this->assertEquals('bar', $globals['foo']);
+
+        // globals can be modified after runtime init
+        $twig = new Twig_Environment(new Twig_Loader_String());
+        $twig->addGlobal('foo', 'foo');
+        $globals = $twig->getGlobals();
+        $twig->initRuntime();
+        $twig->addGlobal('foo', 'bar');
+        $globals = $twig->getGlobals();
+        $this->assertEquals('bar', $globals['foo']);
+
+        // globals can be modified after extensions init
+        $twig = new Twig_Environment(new Twig_Loader_String());
+        $twig->addGlobal('foo', 'foo');
+        $globals = $twig->getGlobals();
+        $twig->getFunctions();
+        $twig->addGlobal('foo', 'bar');
+        $globals = $twig->getGlobals();
+        $this->assertEquals('bar', $globals['foo']);
+
+        // globals can be modified after extensions and runtime init
+        $twig = new Twig_Environment(new Twig_Loader_String());
+        $twig->addGlobal('foo', 'foo');
+        $globals = $twig->getGlobals();
+        $twig->getFunctions();
+        $twig->initRuntime();
+        $twig->addGlobal('foo', 'bar');
+        $globals = $twig->getGlobals();
+        $this->assertEquals('bar', $globals['foo']);
+
+        $twig = new Twig_Environment(new Twig_Loader_String());
+        $twig->getGlobals();
+        $twig->addGlobal('foo', 'bar');
+        $template = $twig->loadTemplate('{{foo}}');
+        $this->assertEquals('bar', $template->render(array()));
+
+        /* to be uncomment in Twig 2.0
+        // globals cannot be added after runtime init
+        $twig = new Twig_Environment(new Twig_Loader_String());
+        $twig->addGlobal('foo', 'foo');
+        $globals = $twig->getGlobals();
+        $twig->initRuntime();
+        try {
+            $twig->addGlobal('bar', 'bar');
+            $this->fail();
+        } catch (LogicException $e) {
+            $this->assertFalse(array_key_exists('bar', $twig->getGlobals()));
+        }
+
+        // globals cannot be added after extensions init
+        $twig = new Twig_Environment(new Twig_Loader_String());
+        $twig->addGlobal('foo', 'foo');
+        $globals = $twig->getGlobals();
+        $twig->getFunctions();
+        try {
+            $twig->addGlobal('bar', 'bar');
+            $this->fail();
+        } catch (LogicException $e) {
+            $this->assertFalse(array_key_exists('bar', $twig->getGlobals()));
+        }
+
+        // globals cannot be added after extensions and runtime init
+        $twig = new Twig_Environment(new Twig_Loader_String());
+        $twig->addGlobal('foo', 'foo');
+        $globals = $twig->getGlobals();
+        $twig->getFunctions();
+        $twig->initRuntime();
+        try {
+            $twig->addGlobal('bar', 'bar');
+            $this->fail();
+        } catch (LogicException $e) {
+            $this->assertFalse(array_key_exists('bar', $twig->getGlobals()));
+        }
+
+        // test adding globals after initRuntime without call to getGlobals
+        $twig = new Twig_Environment(new Twig_Loader_String());
+        $twig->initRuntime();
+        try {
+            $twig->addGlobal('bar', 'bar');
+            $this->fail();
+        } catch (LogicException $e) {
+            $this->assertFalse(array_key_exists('bar', $twig->getGlobals()));
+        }
+        */
+    }
+
+    public function testExtensionsAreNotInitializedWhenRenderingACompiledTemplate()
+    {
+        $options = array('cache' => sys_get_temp_dir().'/twig', 'auto_reload' => false, 'debug' => false);
+
+        // force compilation
+        $twig = new Twig_Environment(new Twig_Loader_String(), $options);
+        $cache = $twig->getCacheFilename('{{ foo }}');
+        if (!is_dir(dirname($cache))) {
+            mkdir(dirname($cache), 0777, true);
+        }
+        file_put_contents($cache, $twig->compileSource('{{ foo }}', '{{ foo }}'));
+
+        // check that extensions won't be initialized when rendering a template that is already in the cache
+        $twig = $this
+            ->getMockBuilder('Twig_Environment')
+            ->setConstructorArgs(array(new Twig_Loader_String(), $options))
+            ->setMethods(array('initExtensions'))
+            ->getMock()
+        ;
+
+        $twig->expects($this->never())->method('initExtensions');
+
+        // render template
+        $output = $twig->render('{{ foo }}', array('foo' => 'bar'));
+        $this->assertEquals('bar', $output);
+
+        unlink($cache);
+    }
+
+    public function testAddExtension()
+    {
+        $twig = new Twig_Environment(new Twig_Loader_String());
+        $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension());
+
+        $this->assertArrayHasKey('test', $twig->getTags());
+        $this->assertArrayHasKey('foo_filter', $twig->getFilters());
+        $this->assertArrayHasKey('foo_function', $twig->getFunctions());
+        $this->assertArrayHasKey('foo_test', $twig->getTests());
+        $this->assertArrayHasKey('foo_unary', $twig->getUnaryOperators());
+        $this->assertArrayHasKey('foo_binary', $twig->getBinaryOperators());
+        $this->assertArrayHasKey('foo_global', $twig->getGlobals());
+        $visitors = $twig->getNodeVisitors();
+        $this->assertEquals('Twig_Tests_EnvironmentTest_NodeVisitor', get_class($visitors[2]));
+    }
+
+    public function testRemoveExtension()
+    {
+        $twig = new Twig_Environment(new Twig_Loader_String());
+        $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension());
+        $twig->removeExtension('environment_test');
+
+        $this->assertFalse(array_key_exists('test', $twig->getTags()));
+        $this->assertFalse(array_key_exists('foo_filter', $twig->getFilters()));
+        $this->assertFalse(array_key_exists('foo_function', $twig->getFunctions()));
+        $this->assertFalse(array_key_exists('foo_test', $twig->getTests()));
+        $this->assertFalse(array_key_exists('foo_unary', $twig->getUnaryOperators()));
+        $this->assertFalse(array_key_exists('foo_binary', $twig->getBinaryOperators()));
+        $this->assertFalse(array_key_exists('foo_global', $twig->getGlobals()));
+        $this->assertCount(2, $twig->getNodeVisitors());
+    }
+}
+
+class Twig_Tests_EnvironmentTest_Extension extends Twig_Extension
+{
+    public function getTokenParsers()
+    {
+        return array(
+            new Twig_Tests_EnvironmentTest_TokenParser(),
+        );
+    }
+
+    public function getNodeVisitors()
+    {
+        return array(
+            new Twig_Tests_EnvironmentTest_NodeVisitor(),
+        );
+    }
+
+    public function getFilters()
+    {
+        return array(
+            'foo_filter' => new Twig_Filter_Function('foo_filter'),
+        );
+    }
+
+    public function getTests()
+    {
+        return array(
+            'foo_test' => new Twig_Test_Function('foo_test'),
+        );
+    }
+
+    public function getFunctions()
+    {
+        return array(
+            'foo_function' => new Twig_Function_Function('foo_function'),
+        );
+    }
+
+    public function getOperators()
+    {
+        return array(
+            array('foo_unary' => array()),
+            array('foo_binary' => array()),
+        );
+    }
+
+    public function getGlobals()
+    {
+        return array(
+            'foo_global' => 'foo_global',
+        );
+    }
+
+    public function getName()
+    {
+        return 'environment_test';
+    }
+}
+
+class Twig_Tests_EnvironmentTest_TokenParser extends Twig_TokenParser
+{
+    public function parse(Twig_Token $token)
+    {
+    }
+
+    public function getTag()
+    {
+        return 'test';
+    }
+}
+
+class Twig_Tests_EnvironmentTest_NodeVisitor implements Twig_NodeVisitorInterface
+{
+    public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
+    {
+        return $node;
+    }
+
+    public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
+    {
+        return $node;
+    }
+
+    public function getPriority()
+    {
+        return 0;
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/ErrorTest.php b/vendor/twig/twig/test/Twig/Tests/ErrorTest.php
new file mode 100644 (file)
index 0000000..9b28697
--- /dev/null
@@ -0,0 +1,159 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_ErrorTest extends PHPUnit_Framework_TestCase
+{
+    public function testErrorWithObjectFilename()
+    {
+        $error = new Twig_Error('foo');
+        $error->setTemplateFile(new SplFileInfo(__FILE__));
+
+        $this->assertContains('test'.DIRECTORY_SEPARATOR.'Twig'.DIRECTORY_SEPARATOR.'Tests'.DIRECTORY_SEPARATOR.'ErrorTest.php', $error->getMessage());
+    }
+
+    public function testErrorWithArrayFilename()
+    {
+        $error = new Twig_Error('foo');
+        $error->setTemplateFile(array('foo' => 'bar'));
+
+        $this->assertEquals('foo in {"foo":"bar"}', $error->getMessage());
+    }
+
+    public function testTwigExceptionAddsFileAndLineWhenMissing()
+    {
+        $loader = new Twig_Loader_Array(array('index' => "\n\n{{ foo.bar }}\n\n\n{{ 'foo' }}"));
+        $twig = new Twig_Environment($loader, array('strict_variables' => true, 'debug' => true, 'cache' => false));
+
+        $template = $twig->loadTemplate('index');
+
+        try {
+            $template->render(array());
+
+            $this->fail();
+        } catch (Twig_Error_Runtime $e) {
+            $this->assertEquals('Variable "foo" does not exist in "index" at line 3', $e->getMessage());
+            $this->assertEquals(3, $e->getTemplateLine());
+            $this->assertEquals('index', $e->getTemplateFile());
+        }
+    }
+
+    public function testRenderWrapsExceptions()
+    {
+        $loader = new Twig_Loader_Array(array('index' => "\n\n\n{{ foo.bar }}\n\n\n\n{{ 'foo' }}"));
+        $twig = new Twig_Environment($loader, array('strict_variables' => true, 'debug' => true, 'cache' => false));
+
+        $template = $twig->loadTemplate('index');
+
+        try {
+            $template->render(array('foo' => new Twig_Tests_ErrorTest_Foo()));
+
+            $this->fail();
+        } catch (Twig_Error_Runtime $e) {
+            $this->assertEquals('An exception has been thrown during the rendering of a template ("Runtime error...") in "index" at line 4.', $e->getMessage());
+            $this->assertEquals(4, $e->getTemplateLine());
+            $this->assertEquals('index', $e->getTemplateFile());
+        }
+    }
+
+    public function testTwigExceptionAddsFileAndLineWhenMissingWithInheritance()
+    {
+        $loader = new Twig_Loader_Array(array(
+            'index' => "{% extends 'base' %}
+            {% block content %}
+                {{ foo.bar }}
+            {% endblock %}
+            {% block foo %}
+                {{ foo.bar }}
+            {% endblock %}",
+            'base' => '{% block content %}{% endblock %}'
+        ));
+        $twig = new Twig_Environment($loader, array('strict_variables' => true, 'debug' => true, 'cache' => false));
+
+        $template = $twig->loadTemplate('index');
+        try {
+            $template->render(array());
+
+            $this->fail();
+        } catch (Twig_Error_Runtime $e) {
+            $this->assertEquals('Variable "foo" does not exist in "index" at line 3', $e->getMessage());
+            $this->assertEquals(3, $e->getTemplateLine());
+            $this->assertEquals('index', $e->getTemplateFile());
+        }
+
+        try {
+            $template->render(array('foo' => new Twig_Tests_ErrorTest_Foo()));
+
+            $this->fail();
+        } catch (Twig_Error_Runtime $e) {
+            $this->assertEquals('An exception has been thrown during the rendering of a template ("Runtime error...") in "index" at line 3.', $e->getMessage());
+            $this->assertEquals(3, $e->getTemplateLine());
+            $this->assertEquals('index', $e->getTemplateFile());
+        }
+    }
+
+    public function testTwigExceptionAddsFileAndLineWhenMissingWithInheritanceAgain()
+    {
+        $loader = new Twig_Loader_Array(array(
+            'index' => "{% extends 'base' %}
+            {% block content %}
+                {{ parent() }}
+            {% endblock %}",
+            'base' => '{% block content %}{{ foo }}{% endblock %}'
+        ));
+        $twig = new Twig_Environment($loader, array('strict_variables' => true, 'debug' => true, 'cache' => false));
+
+        $template = $twig->loadTemplate('index');
+        try {
+            $template->render(array());
+
+            $this->fail();
+        } catch (Twig_Error_Runtime $e) {
+            $this->assertEquals('Variable "foo" does not exist in "base" at line 1', $e->getMessage());
+            $this->assertEquals(1, $e->getTemplateLine());
+            $this->assertEquals('base', $e->getTemplateFile());
+        }
+    }
+
+    public function testTwigExceptionAddsFileAndLineWhenMissingWithInheritanceOnDisk()
+    {
+        $loader = new Twig_Loader_Filesystem(dirname(__FILE__).'/Fixtures/errors');
+        $twig = new Twig_Environment($loader, array('strict_variables' => true, 'debug' => true, 'cache' => false));
+
+        $template = $twig->loadTemplate('index.html');
+        try {
+            $template->render(array());
+
+            $this->fail();
+        } catch (Twig_Error_Runtime $e) {
+            $this->assertEquals('Variable "foo" does not exist in "index.html" at line 3', $e->getMessage());
+            $this->assertEquals(3, $e->getTemplateLine());
+            $this->assertEquals('index.html', $e->getTemplateFile());
+        }
+
+        try {
+            $template->render(array('foo' => new Twig_Tests_ErrorTest_Foo()));
+
+            $this->fail();
+        } catch (Twig_Error_Runtime $e) {
+            $this->assertEquals('An exception has been thrown during the rendering of a template ("Runtime error...") in "index.html" at line 3.', $e->getMessage());
+            $this->assertEquals(3, $e->getTemplateLine());
+            $this->assertEquals('index.html', $e->getTemplateFile());
+        }
+    }
+}
+
+class Twig_Tests_ErrorTest_Foo
+{
+    public function bar()
+    {
+        throw new Exception('Runtime error...');
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/ExpressionParserTest.php b/vendor/twig/twig/test/Twig/Tests/ExpressionParserTest.php
new file mode 100644 (file)
index 0000000..8ec6537
--- /dev/null
@@ -0,0 +1,332 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase
+{
+    /**
+     * @expectedException Twig_Error_Syntax
+     * @dataProvider getFailingTestsForAssignment
+     */
+    public function testCanOnlyAssignToNames($template)
+    {
+        $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
+        $parser = new Twig_Parser($env);
+
+        $parser->parse($env->tokenize($template, 'index'));
+    }
+
+    public function getFailingTestsForAssignment()
+    {
+        return array(
+            array('{% set false = "foo" %}'),
+            array('{% set true = "foo" %}'),
+            array('{% set none = "foo" %}'),
+            array('{% set 3 = "foo" %}'),
+            array('{% set 1 + 2 = "foo" %}'),
+            array('{% set "bar" = "foo" %}'),
+            array('{% set %}{% endset %}')
+        );
+    }
+
+    /**
+     * @dataProvider getTestsForArray
+     */
+    public function testArrayExpression($template, $expected)
+    {
+        $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
+        $stream = $env->tokenize($template, 'index');
+        $parser = new Twig_Parser($env);
+
+        $this->assertEquals($expected, $parser->parse($stream)->getNode('body')->getNode(0)->getNode('expr'));
+    }
+
+    /**
+     * @expectedException Twig_Error_Syntax
+     * @dataProvider getFailingTestsForArray
+     */
+    public function testArraySyntaxError($template)
+    {
+        $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
+        $parser = new Twig_Parser($env);
+
+        $parser->parse($env->tokenize($template, 'index'));
+    }
+
+    public function getFailingTestsForArray()
+    {
+        return array(
+            array('{{ [1, "a": "b"] }}'),
+            array('{{ {"a": "b", 2} }}'),
+        );
+    }
+
+    public function getTestsForArray()
+    {
+        return array(
+            // simple array
+            array('{{ [1, 2] }}', new Twig_Node_Expression_Array(array(
+                  new Twig_Node_Expression_Constant(0, 1),
+                  new Twig_Node_Expression_Constant(1, 1),
+
+                  new Twig_Node_Expression_Constant(1, 1),
+                  new Twig_Node_Expression_Constant(2, 1),
+                ), 1),
+            ),
+
+            // array with trailing ,
+            array('{{ [1, 2, ] }}', new Twig_Node_Expression_Array(array(
+                  new Twig_Node_Expression_Constant(0, 1),
+                  new Twig_Node_Expression_Constant(1, 1),
+
+                  new Twig_Node_Expression_Constant(1, 1),
+                  new Twig_Node_Expression_Constant(2, 1),
+                ), 1),
+            ),
+
+            // simple hash
+            array('{{ {"a": "b", "b": "c"} }}', new Twig_Node_Expression_Array(array(
+                  new Twig_Node_Expression_Constant('a', 1),
+                  new Twig_Node_Expression_Constant('b', 1),
+
+                  new Twig_Node_Expression_Constant('b', 1),
+                  new Twig_Node_Expression_Constant('c', 1),
+                ), 1),
+            ),
+
+            // hash with trailing ,
+            array('{{ {"a": "b", "b": "c", } }}', new Twig_Node_Expression_Array(array(
+                  new Twig_Node_Expression_Constant('a', 1),
+                  new Twig_Node_Expression_Constant('b', 1),
+
+                  new Twig_Node_Expression_Constant('b', 1),
+                  new Twig_Node_Expression_Constant('c', 1),
+                ), 1),
+            ),
+
+            // hash in an array
+            array('{{ [1, {"a": "b", "b": "c"}] }}', new Twig_Node_Expression_Array(array(
+                  new Twig_Node_Expression_Constant(0, 1),
+                  new Twig_Node_Expression_Constant(1, 1),
+
+                  new Twig_Node_Expression_Constant(1, 1),
+                  new Twig_Node_Expression_Array(array(
+                        new Twig_Node_Expression_Constant('a', 1),
+                        new Twig_Node_Expression_Constant('b', 1),
+
+                        new Twig_Node_Expression_Constant('b', 1),
+                        new Twig_Node_Expression_Constant('c', 1),
+                      ), 1),
+                ), 1),
+            ),
+
+            // array in a hash
+            array('{{ {"a": [1, 2], "b": "c"} }}', new Twig_Node_Expression_Array(array(
+                  new Twig_Node_Expression_Constant('a', 1),
+                  new Twig_Node_Expression_Array(array(
+                        new Twig_Node_Expression_Constant(0, 1),
+                        new Twig_Node_Expression_Constant(1, 1),
+
+                        new Twig_Node_Expression_Constant(1, 1),
+                        new Twig_Node_Expression_Constant(2, 1),
+                      ), 1),
+                  new Twig_Node_Expression_Constant('b', 1),
+                  new Twig_Node_Expression_Constant('c', 1),
+                ), 1),
+            ),
+        );
+    }
+
+    /**
+     * @expectedException Twig_Error_Syntax
+     */
+    public function testStringExpressionDoesNotConcatenateTwoConsecutiveStrings()
+    {
+        $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false, 'optimizations' => 0));
+        $stream = $env->tokenize('{{ "a" "b" }}', 'index');
+        $parser = new Twig_Parser($env);
+
+        $parser->parse($stream);
+    }
+
+    /**
+     * @dataProvider getTestsForString
+     */
+    public function testStringExpression($template, $expected)
+    {
+        $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false, 'optimizations' => 0));
+        $stream = $env->tokenize($template, 'index');
+        $parser = new Twig_Parser($env);
+
+        $this->assertEquals($expected, $parser->parse($stream)->getNode('body')->getNode(0)->getNode('expr'));
+    }
+
+    public function getTestsForString()
+    {
+        return array(
+            array(
+                '{{ "foo" }}', new Twig_Node_Expression_Constant('foo', 1),
+            ),
+            array(
+                '{{ "foo #{bar}" }}', new Twig_Node_Expression_Binary_Concat(
+                    new Twig_Node_Expression_Constant('foo ', 1),
+                    new Twig_Node_Expression_Name('bar', 1),
+                    1
+                ),
+            ),
+            array(
+                '{{ "foo #{bar} baz" }}', new Twig_Node_Expression_Binary_Concat(
+                    new Twig_Node_Expression_Binary_Concat(
+                        new Twig_Node_Expression_Constant('foo ', 1),
+                        new Twig_Node_Expression_Name('bar', 1),
+                        1
+                    ),
+                    new Twig_Node_Expression_Constant(' baz', 1),
+                    1
+                )
+            ),
+
+            array(
+                '{{ "foo #{"foo #{bar} baz"} baz" }}', new Twig_Node_Expression_Binary_Concat(
+                    new Twig_Node_Expression_Binary_Concat(
+                        new Twig_Node_Expression_Constant('foo ', 1),
+                        new Twig_Node_Expression_Binary_Concat(
+                            new Twig_Node_Expression_Binary_Concat(
+                                new Twig_Node_Expression_Constant('foo ', 1),
+                                new Twig_Node_Expression_Name('bar', 1),
+                                1
+                            ),
+                            new Twig_Node_Expression_Constant(' baz', 1),
+                            1
+                        ),
+                        1
+                    ),
+                    new Twig_Node_Expression_Constant(' baz', 1),
+                    1
+                ),
+            ),
+        );
+    }
+
+    /**
+     * @expectedException Twig_Error_Syntax
+     */
+    public function testAttributeCallDoesNotSupportNamedArguments()
+    {
+        $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
+        $parser = new Twig_Parser($env);
+
+        $parser->parse($env->tokenize('{{ foo.bar(name="Foo") }}', 'index'));
+    }
+
+    /**
+     * @expectedException Twig_Error_Syntax
+     */
+    public function testMacroCallDoesNotSupportNamedArguments()
+    {
+        $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
+        $parser = new Twig_Parser($env);
+
+        $parser->parse($env->tokenize('{% from _self import foo %}{% macro foo() %}{% endmacro %}{{ foo(name="Foo") }}', 'index'));
+    }
+
+    /**
+     * @expectedException        Twig_Error_Syntax
+     * @expectedExceptionMessage An argument must be a name. Unexpected token "string" of value "a" ("name" expected) in "index" at line 1
+     */
+    public function testMacroDefinitionDoesNotSupportNonNameVariableName()
+    {
+        $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
+        $parser = new Twig_Parser($env);
+
+        $parser->parse($env->tokenize('{% macro foo("a") %}{% endmacro %}', 'index'));
+    }
+
+    /**
+     * @expectedException        Twig_Error_Syntax
+     * @expectedExceptionMessage A default value for an argument must be a constant (a boolean, a string, a number, or an array) in "index" at line 1
+     * @dataProvider             getMacroDefinitionDoesNotSupportNonConstantDefaultValues
+     */
+    public function testMacroDefinitionDoesNotSupportNonConstantDefaultValues($template)
+    {
+        $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
+        $parser = new Twig_Parser($env);
+
+        $parser->parse($env->tokenize($template, 'index'));
+    }
+
+    public function getMacroDefinitionDoesNotSupportNonConstantDefaultValues()
+    {
+        return array(
+            array('{% macro foo(name = "a #{foo} a") %}{% endmacro %}'),
+            array('{% macro foo(name = [["b", "a #{foo} a"]]) %}{% endmacro %}'),
+        );
+    }
+
+    /**
+     * @dataProvider getMacroDefinitionSupportsConstantDefaultValues
+     */
+    public function testMacroDefinitionSupportsConstantDefaultValues($template)
+    {
+        $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
+        $parser = new Twig_Parser($env);
+
+        $parser->parse($env->tokenize($template, 'index'));
+    }
+
+    public function getMacroDefinitionSupportsConstantDefaultValues()
+    {
+        return array(
+            array('{% macro foo(name = "aa") %}{% endmacro %}'),
+            array('{% macro foo(name = 12) %}{% endmacro %}'),
+            array('{% macro foo(name = true) %}{% endmacro %}'),
+            array('{% macro foo(name = ["a"]) %}{% endmacro %}'),
+            array('{% macro foo(name = [["a"]]) %}{% endmacro %}'),
+            array('{% macro foo(name = {a: "a"}) %}{% endmacro %}'),
+            array('{% macro foo(name = {a: {b: "a"}}) %}{% endmacro %}'),
+        );
+    }
+
+    /**
+     * @expectedException        Twig_Error_Syntax
+     * @expectedExceptionMessage The function "cycl" does not exist. Did you mean "cycle" in "index" at line 1
+     */
+    public function testUnknownFunction()
+    {
+        $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
+        $parser = new Twig_Parser($env);
+
+        $parser->parse($env->tokenize('{{ cycl() }}', 'index'));
+    }
+
+    /**
+     * @expectedException        Twig_Error_Syntax
+     * @expectedExceptionMessage The filter "lowe" does not exist. Did you mean "lower" in "index" at line 1
+     */
+    public function testUnknownFilter()
+    {
+        $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
+        $parser = new Twig_Parser($env);
+
+        $parser->parse($env->tokenize('{{ 1|lowe }}', 'index'));
+    }
+
+    /**
+     * @expectedException        Twig_Error_Syntax
+     * @expectedExceptionMessage The test "nul" does not exist. Did you mean "null" in "index" at line 1
+     */
+    public function testUnknownTest()
+    {
+        $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
+        $parser = new Twig_Parser($env);
+
+        $parser->parse($env->tokenize('{{ 1 is nul }}', 'index'));
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Extension/CoreTest.php b/vendor/twig/twig/test/Twig/Tests/Extension/CoreTest.php
new file mode 100644 (file)
index 0000000..5743e34
--- /dev/null
@@ -0,0 +1,117 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Extension_CoreTest extends PHPUnit_Framework_TestCase
+{
+    /**
+     * @dataProvider getRandomFunctionTestData
+     */
+    public function testRandomFunction($value, $expectedInArray)
+    {
+        $env = new Twig_Environment();
+
+        for ($i = 0; $i < 100; $i++) {
+            $this->assertTrue(in_array(twig_random($env, $value), $expectedInArray, true)); // assertContains() would not consider the type
+        }
+    }
+
+    public function getRandomFunctionTestData()
+    {
+        return array(
+            array( // array
+                array('apple', 'orange', 'citrus'),
+                array('apple', 'orange', 'citrus'),
+            ),
+            array( // Traversable
+                new ArrayObject(array('apple', 'orange', 'citrus')),
+                array('apple', 'orange', 'citrus'),
+            ),
+            array( // unicode string
+                'Ä€é',
+                array('Ä', '€', 'é'),
+            ),
+            array( // numeric but string
+                '123',
+                array('1', '2', '3'),
+            ),
+            array( // integer
+                5,
+                range(0, 5, 1),
+            ),
+            array( // float
+                5.9,
+                range(0, 5, 1),
+            ),
+            array( // negative
+                -2,
+                array(0, -1, -2),
+            ),
+        );
+    }
+
+    public function testRandomFunctionWithoutParameter()
+    {
+        $max = mt_getrandmax();
+
+        for ($i = 0; $i < 100; $i++) {
+            $val = twig_random(new Twig_Environment());
+            $this->assertTrue(is_int($val) && $val >= 0 && $val <= $max);
+        }
+    }
+
+    public function testRandomFunctionReturnsAsIs()
+    {
+        $this->assertSame('', twig_random(new Twig_Environment(), ''));
+        $this->assertSame('', twig_random(new Twig_Environment(null, array('charset' => null)), ''));
+
+        $instance = new stdClass();
+        $this->assertSame($instance, twig_random(new Twig_Environment(), $instance));
+    }
+
+    /**
+     * @expectedException Twig_Error_Runtime
+     */
+    public function testRandomFunctionOfEmptyArrayThrowsException()
+    {
+        twig_random(new Twig_Environment(), array());
+    }
+
+    public function testRandomFunctionOnNonUTF8String()
+    {
+        if (!function_exists('iconv') && !function_exists('mb_convert_encoding')) {
+            $this->markTestSkipped('needs iconv or mbstring');
+        }
+
+        $twig = new Twig_Environment();
+        $twig->setCharset('ISO-8859-1');
+
+        $text = twig_convert_encoding('Äé', 'ISO-8859-1', 'UTF-8');
+        for ($i = 0; $i < 30; $i++) {
+            $rand = twig_random($twig, $text);
+            $this->assertTrue(in_array(twig_convert_encoding($rand, 'UTF-8', 'ISO-8859-1'), array('Ä', 'é'), true));
+        }
+    }
+
+    public function testReverseFilterOnNonUTF8String()
+    {
+        if (!function_exists('iconv') && !function_exists('mb_convert_encoding')) {
+            $this->markTestSkipped('needs iconv or mbstring');
+        }
+
+        $twig = new Twig_Environment();
+        $twig->setCharset('ISO-8859-1');
+
+        $input = twig_convert_encoding('Äé', 'ISO-8859-1', 'UTF-8');
+        $output = twig_convert_encoding(twig_reverse_filter($twig, $input), 'UTF-8', 'ISO-8859-1');
+
+        $this->assertEquals($output, 'éÄ');
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Extension/SandboxTest.php b/vendor/twig/twig/test/Twig/Tests/Extension/SandboxTest.php
new file mode 100644 (file)
index 0000000..72253c8
--- /dev/null
@@ -0,0 +1,212 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Extension_SandboxTest extends PHPUnit_Framework_TestCase
+{
+    protected static $params, $templates;
+
+    public function setUp()
+    {
+        self::$params = array(
+            'name' => 'Fabien',
+            'obj'  => new FooObject(),
+            'arr'  => array('obj' => new FooObject()),
+        );
+
+        self::$templates = array(
+            '1_basic1' => '{{ obj.foo }}',
+            '1_basic2' => '{{ name|upper }}',
+            '1_basic3' => '{% if name %}foo{% endif %}',
+            '1_basic4' => '{{ obj.bar }}',
+            '1_basic5' => '{{ obj }}',
+            '1_basic6' => '{{ arr.obj }}',
+            '1_basic7' => '{{ cycle(["foo","bar"], 1) }}',
+            '1_basic8' => '{{ obj.getfoobar }}{{ obj.getFooBar }}',
+            '1_basic'  => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}',
+            '1_layout' => '{% block content %}{% endblock %}',
+            '1_child'  => '{% extends "1_layout" %}{% block content %}{{ "a"|json_encode }}{% endblock %}',
+        );
+    }
+
+    /**
+     * @expectedException        Twig_Sandbox_SecurityError
+     * @expectedExceptionMessage Filter "json_encode" is not allowed in "1_child".
+     */
+    public function testSandboxWithInheritance()
+    {
+        $twig = $this->getEnvironment(true, array(), self::$templates, array('block'));
+        $twig->loadTemplate('1_child')->render(array());
+    }
+
+    public function testSandboxGloballySet()
+    {
+        $twig = $this->getEnvironment(false, array(), self::$templates);
+        $this->assertEquals('FOO', $twig->loadTemplate('1_basic')->render(self::$params), 'Sandbox does nothing if it is disabled globally');
+
+        $twig = $this->getEnvironment(true, array(), self::$templates);
+        try {
+            $twig->loadTemplate('1_basic1')->render(self::$params);
+            $this->fail('Sandbox throws a SecurityError exception if an unallowed method is called');
+        } catch (Twig_Sandbox_SecurityError $e) {
+        }
+
+        $twig = $this->getEnvironment(true, array(), self::$templates);
+        try {
+            $twig->loadTemplate('1_basic2')->render(self::$params);
+            $this->fail('Sandbox throws a SecurityError exception if an unallowed filter is called');
+        } catch (Twig_Sandbox_SecurityError $e) {
+        }
+
+        $twig = $this->getEnvironment(true, array(), self::$templates);
+        try {
+            $twig->loadTemplate('1_basic3')->render(self::$params);
+            $this->fail('Sandbox throws a SecurityError exception if an unallowed tag is used in the template');
+        } catch (Twig_Sandbox_SecurityError $e) {
+        }
+
+        $twig = $this->getEnvironment(true, array(), self::$templates);
+        try {
+            $twig->loadTemplate('1_basic4')->render(self::$params);
+            $this->fail('Sandbox throws a SecurityError exception if an unallowed property is called in the template');
+        } catch (Twig_Sandbox_SecurityError $e) {
+        }
+
+        $twig = $this->getEnvironment(true, array(), self::$templates);
+        try {
+            $twig->loadTemplate('1_basic5')->render(self::$params);
+            $this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template');
+        } catch (Twig_Sandbox_SecurityError $e) {
+        }
+
+        $twig = $this->getEnvironment(true, array(), self::$templates);
+        try {
+            $twig->loadTemplate('1_basic6')->render(self::$params);
+            $this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template');
+        } catch (Twig_Sandbox_SecurityError $e) {
+        }
+
+        $twig = $this->getEnvironment(true, array(), self::$templates);
+        try {
+            $twig->loadTemplate('1_basic7')->render(self::$params);
+            $this->fail('Sandbox throws a SecurityError exception if an unallowed function is called in the template');
+        } catch (Twig_Sandbox_SecurityError $e) {
+        }
+
+        $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => 'foo'));
+        FooObject::reset();
+        $this->assertEquals('foo', $twig->loadTemplate('1_basic1')->render(self::$params), 'Sandbox allow some methods');
+        $this->assertEquals(1, FooObject::$called['foo'], 'Sandbox only calls method once');
+
+        $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => '__toString'));
+        FooObject::reset();
+        $this->assertEquals('foo', $twig->loadTemplate('1_basic5')->render(self::$params), 'Sandbox allow some methods');
+        $this->assertEquals(1, FooObject::$called['__toString'], 'Sandbox only calls method once');
+
+        $twig = $this->getEnvironment(true, array(), self::$templates, array(), array('upper'));
+        $this->assertEquals('FABIEN', $twig->loadTemplate('1_basic2')->render(self::$params), 'Sandbox allow some filters');
+
+        $twig = $this->getEnvironment(true, array(), self::$templates, array('if'));
+        $this->assertEquals('foo', $twig->loadTemplate('1_basic3')->render(self::$params), 'Sandbox allow some tags');
+
+        $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array(), array('FooObject' => 'bar'));
+        $this->assertEquals('bar', $twig->loadTemplate('1_basic4')->render(self::$params), 'Sandbox allow some properties');
+
+        $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array(), array(), array('cycle'));
+        $this->assertEquals('bar', $twig->loadTemplate('1_basic7')->render(self::$params), 'Sandbox allow some functions');
+
+        foreach (array('getfoobar', 'getFoobar', 'getFooBar') as $name) {
+            $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => $name));
+            FooObject::reset();
+            $this->assertEquals('foobarfoobar', $twig->loadTemplate('1_basic8')->render(self::$params), 'Sandbox allow methods in a case-insensitive way');
+            $this->assertEquals(2, FooObject::$called['getFooBar'], 'Sandbox only calls method once');
+        }
+    }
+
+    public function testSandboxLocallySetForAnInclude()
+    {
+        self::$templates = array(
+            '2_basic'    => '{{ obj.foo }}{% include "2_included" %}{{ obj.foo }}',
+            '2_included' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}',
+        );
+
+        $twig = $this->getEnvironment(false, array(), self::$templates);
+        $this->assertEquals('fooFOOfoo', $twig->loadTemplate('2_basic')->render(self::$params), 'Sandbox does nothing if disabled globally and sandboxed not used for the include');
+
+        self::$templates = array(
+            '3_basic'    => '{{ obj.foo }}{% sandbox %}{% include "3_included" %}{% endsandbox %}{{ obj.foo }}',
+            '3_included' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}',
+        );
+
+        $twig = $this->getEnvironment(true, array(), self::$templates);
+        try {
+            $twig->loadTemplate('3_basic')->render(self::$params);
+            $this->fail('Sandbox throws a SecurityError exception when the included file is sandboxed');
+        } catch (Twig_Sandbox_SecurityError $e) {
+        }
+    }
+
+    public function testMacrosInASandbox()
+    {
+        $twig = $this->getEnvironment(true, array('autoescape' => true), array('index' => <<<EOF
+{%- import _self as macros %}
+
+{%- macro test(text) %}<p>{{ text }}</p>{% endmacro %}
+
+{{- macros.test('username') }}
+EOF
+        ), array('macro', 'import'), array('escape'));
+
+        $this->assertEquals('<p>username</p>', $twig->loadTemplate('index')->render(array()));
+    }
+
+    protected function getEnvironment($sandboxed, $options, $templates, $tags = array(), $filters = array(), $methods = array(), $properties = array(), $functions = array())
+    {
+        $loader = new Twig_Loader_Array($templates);
+        $twig = new Twig_Environment($loader, array_merge(array('debug' => true, 'cache' => false, 'autoescape' => false), $options));
+        $policy = new Twig_Sandbox_SecurityPolicy($tags, $filters, $methods, $properties, $functions);
+        $twig->addExtension(new Twig_Extension_Sandbox($policy, $sandboxed));
+
+        return $twig;
+    }
+}
+
+class FooObject
+{
+    public static $called = array('__toString' => 0, 'foo' => 0, 'getFooBar' => 0);
+
+    public $bar = 'bar';
+
+    public static function reset()
+    {
+        self::$called = array('__toString' => 0, 'foo' => 0, 'getFooBar' => 0);
+    }
+
+    public function __toString()
+    {
+        ++self::$called['__toString'];
+
+        return 'foo';
+    }
+
+    public function foo()
+    {
+        ++self::$called['foo'];
+
+        return 'foo';
+    }
+
+    public function getFooBar()
+    {
+        ++self::$called['getFooBar'];
+
+        return 'foobar';
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/FileCachingTest.php b/vendor/twig/twig/test/Twig/Tests/FileCachingTest.php
new file mode 100644 (file)
index 0000000..8efc948
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+
+class Twig_Tests_FileCachingTest extends PHPUnit_Framework_TestCase
+{
+    protected $fileName;
+    protected $env;
+    protected $tmpDir;
+
+    public function setUp()
+    {
+        $this->tmpDir = sys_get_temp_dir().'/TwigTests';
+        if (!file_exists($this->tmpDir)) {
+            @mkdir($this->tmpDir, 0777, true);
+        }
+
+        if (!is_writable($this->tmpDir)) {
+            $this->markTestSkipped(sprintf('Unable to run the tests as "%s" is not writable.', $this->tmpDir));
+        }
+
+        $this->env = new Twig_Environment(new Twig_Loader_String(), array('cache' => $this->tmpDir));
+    }
+
+    public function tearDown()
+    {
+        if ($this->fileName) {
+            unlink($this->fileName);
+        }
+
+        $this->removeDir($this->tmpDir);
+    }
+
+    public function testWritingCacheFiles()
+    {
+        $name = 'This is just text.';
+        $template = $this->env->loadTemplate($name);
+        $cacheFileName = $this->env->getCacheFilename($name);
+
+        $this->assertTrue(file_exists($cacheFileName), 'Cache file does not exist.');
+        $this->fileName = $cacheFileName;
+    }
+
+    public function testClearingCacheFiles()
+    {
+        $name = 'I will be deleted.';
+        $template = $this->env->loadTemplate($name);
+        $cacheFileName = $this->env->getCacheFilename($name);
+
+        $this->assertTrue(file_exists($cacheFileName), 'Cache file does not exist.');
+        $this->env->clearCacheFiles();
+        $this->assertFalse(file_exists($cacheFileName), 'Cache file was not cleared.');
+    }
+
+    private function removeDir($target)
+    {
+        $fp = opendir($target);
+        while (false !== $file = readdir($fp)) {
+            if (in_array($file, array('.', '..'))) {
+                continue;
+            }
+
+            if (is_dir($target.'/'.$file)) {
+                self::removeDir($target.'/'.$file);
+            } else {
+                unlink($target.'/'.$file);
+            }
+        }
+        closedir($fp);
+        rmdir($target);
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/errors/base.html b/vendor/twig/twig/test/Twig/Tests/Fixtures/errors/base.html
new file mode 100644 (file)
index 0000000..cb0dbe4
--- /dev/null
@@ -0,0 +1 @@
+{% block content %}{% endblock %}
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/errors/index.html b/vendor/twig/twig/test/Twig/Tests/Fixtures/errors/index.html
new file mode 100644 (file)
index 0000000..df57c82
--- /dev/null
@@ -0,0 +1,7 @@
+{% extends 'base.html' %}
+{% block content %}
+    {{ foo.bar }}
+{% endblock %}
+{% block foo %}
+    {{ foo.bar }}
+{% endblock %}
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/unclosed_tag.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/unclosed_tag.test
new file mode 100644 (file)
index 0000000..02245e9
--- /dev/null
@@ -0,0 +1,20 @@
+--TEST--
+Exception for an unclosed tag
+--TEMPLATE--
+{% block foo %}
+     {% if foo %}
+
+
+
+
+         {% for i in fo %}
+
+
+
+         {% endfor %}
+
+
+
+{% endblock %}
+--EXCEPTION--
+Twig_Error_Syntax: Unexpected tag name "endblock" (expecting closing tag for the "if" tag defined near line 4) in "index.twig" at line 16
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/array.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/array.test
new file mode 100644 (file)
index 0000000..c69b119
--- /dev/null
@@ -0,0 +1,61 @@
+--TEST--
+Twig supports array notation
+--TEMPLATE--
+{# empty array #}
+{{ []|join(',') }}
+
+{{ [1, 2]|join(',') }}
+{{ ['foo', "bar"]|join(',') }}
+{{ {0: 1, 'foo': 'bar'}|join(',') }}
+{{ {0: 1, 'foo': 'bar'}|keys|join(',') }}
+
+{{ {0: 1, foo: 'bar'}|join(',') }}
+{{ {0: 1, foo: 'bar'}|keys|join(',') }}
+
+{# nested arrays #}
+{% set a = [1, 2, [1, 2], {'foo': {'foo': 'bar'}}] %}
+{{ a[2]|join(',') }}
+{{ a[3]["foo"]|join(',') }}
+
+{# works even if [] is used inside the array #}
+{{ [foo[bar]]|join(',') }}
+
+{# elements can be any expression #}
+{{ ['foo'|upper, bar|upper, bar == foo]|join(',') }}
+
+{# arrays can have a trailing , like in PHP #}
+{{
+  [
+    1,
+    2,
+  ]|join(',')
+}}
+
+{# keys can be any expression #}
+{% set a = 1 %}
+{% set b = "foo" %}
+{% set ary = { (a): 'a', (b): 'b', 'c': 'c', (a ~ b): 'd' } %}
+{{ ary|keys|join(',') }}
+{{ ary|join(',') }}
+--DATA--
+return array('bar' => 'bar', 'foo' => array('bar' => 'bar'))
+--EXPECT--
+1,2
+foo,bar
+1,bar
+0,foo
+
+1,bar
+0,foo
+
+1,2
+bar
+
+bar
+
+FOO,BAR,
+
+1,2
+
+1,foo,c,1foo
+a,b,c,d
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/array_call.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/array_call.test
new file mode 100644 (file)
index 0000000..f3df328
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+Twig supports method calls
+--TEMPLATE--
+{{ items.foo }}
+{{ items['foo'] }}
+{{ items[foo] }}
+{{ items[items[foo]] }}
+--DATA--
+return array('foo' => 'bar', 'items' => array('foo' => 'bar', 'bar' => 'foo'))
+--EXPECT--
+bar
+bar
+foo
+bar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/binary.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/binary.test
new file mode 100644 (file)
index 0000000..f5e6845
--- /dev/null
@@ -0,0 +1,46 @@
+--TEST--
+Twig supports binary operations (+, -, *, /, ~, %, and, or)
+--TEMPLATE--
+{{ 1 + 1 }}
+{{ 2 - 1 }}
+{{ 2 * 2 }}
+{{ 2 / 2 }}
+{{ 3 % 2 }}
+{{ 1 and 1 }}
+{{ 1 and 0 }}
+{{ 0 and 1 }}
+{{ 0 and 0 }}
+{{ 1 or 1 }}
+{{ 1 or 0 }}
+{{ 0 or 1 }}
+{{ 0 or 0 }}
+{{ 0 or 1 and 0 }}
+{{ 1 or 0 and 1 }}
+{{ "foo" ~ "bar" }}
+{{ foo ~ "bar" }}
+{{ "foo" ~ bar }}
+{{ foo ~ bar }}
+{{ 20 // 7 }}
+--DATA--
+return array('foo' => 'bar', 'bar' => 'foo')
+--EXPECT--
+2
+1
+4
+1
+1
+1
+
+
+
+1
+1
+1
+
+
+1
+foobar
+barbar
+foofoo
+barfoo
+2
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/bitwise.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/bitwise.test
new file mode 100644 (file)
index 0000000..7b56b76
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+Twig supports bitwise operations
+--TEMPLATE--
+{{ 1 b-and 5 }}
+{{ 1 b-or 5 }}
+{{ 1 b-xor 5 }}
+{{ (1 and 0 b-or 0) is sameas(1 and (0 b-or 0)) ? 'ok' : 'ko' }}
+--DATA--
+return array()
+--EXPECT--
+1
+5
+4
+ok
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/comparison.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/comparison.test
new file mode 100644 (file)
index 0000000..726b850
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+Twig supports comparison operators (==, !=, <, >, >=, <=)
+--TEMPLATE--
+{{ 1 > 2 }}/{{ 1 > 1 }}/{{ 1 >= 2 }}/{{ 1 >= 1 }}
+{{ 1 < 2 }}/{{ 1 < 1 }}/{{ 1 <= 2 }}/{{ 1 <= 1 }}
+{{ 1 == 1 }}/{{ 1 == 2 }}
+{{ 1 != 1 }}/{{ 1 != 2 }}
+--DATA--
+return array()
+--EXPECT--
+///1
+1//1/1
+1/
+/1
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/dotdot.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/dotdot.test
new file mode 100644 (file)
index 0000000..9cd0676
--- /dev/null
@@ -0,0 +1,20 @@
+--TEST--
+Twig supports the .. operator
+--TEMPLATE--
+{% for i in 0..10 %}{{ i }} {% endfor %}
+
+{% for letter in 'a'..'z' %}{{ letter }} {% endfor %}
+
+{% for letter in 'a'|upper..'z'|upper %}{{ letter }} {% endfor %}
+
+{% for i in foo[0]..foo[1] %}{{ i }} {% endfor %}
+
+{% for i in 0 + 1 .. 10 - 1 %}{{ i }} {% endfor %}
+--DATA--
+return array('foo' => array(1, 10))
+--EXPECT--
+0 1 2 3 4 5 6 7 8 9 10 
+a b c d e f g h i j k l m n o p q r s t u v w x y z 
+A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 
+1 2 3 4 5 6 7 8 9 10 
+1 2 3 4 5 6 7 8 9
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/grouping.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/grouping.test
new file mode 100644 (file)
index 0000000..79f8e0b
--- /dev/null
@@ -0,0 +1,8 @@
+--TEST--
+Twig supports grouping of expressions
+--TEMPLATE--
+{{ (2 + 2) / 2 }}
+--DATA--
+return array()
+--EXPECT--
+2
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/literals.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/literals.test
new file mode 100644 (file)
index 0000000..7ae3bae
--- /dev/null
@@ -0,0 +1,22 @@
+--TEST--
+Twig supports literals
+--TEMPLATE--
+1 {{ true }}
+2 {{ TRUE }}
+3 {{ false }}
+4 {{ FALSE }}
+5 {{ none }}
+6 {{ NONE }}
+7 {{ null }}
+8 {{ NULL }}
+--DATA--
+return array()
+--EXPECT--
+1 1
+2 1
+3 
+4 
+5 
+6 
+7 
+8 
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/magic_call.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/magic_call.test
new file mode 100644 (file)
index 0000000..159db96
--- /dev/null
@@ -0,0 +1,27 @@
+--TEST--
+Twig supports __call() for attributes
+--TEMPLATE--
+{{ foo.foo }}
+{{ foo.bar }}
+--DATA--
+class TestClassForMagicCallAttributes
+{
+  public function getBar()
+  {
+    return 'bar_from_getbar';
+  }
+
+  public function __call($method, $arguments)
+  {
+    if ('foo' === $method)
+    {
+      return 'foo_from_call';
+    }
+
+    return false;
+  }
+}
+return array('foo' => new TestClassForMagicCallAttributes())
+--EXPECT--
+foo_from_call
+bar_from_getbar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/method_call.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/method_call.test
new file mode 100644 (file)
index 0000000..5f801e6
--- /dev/null
@@ -0,0 +1,28 @@
+--TEST--
+Twig supports method calls
+--TEMPLATE--
+{{ items.foo.foo }}
+{{ items.foo.getFoo() }}
+{{ items.foo.bar }}
+{{ items.foo['bar'] }}
+{{ items.foo.bar('a', 43) }}
+{{ items.foo.bar(foo) }}
+{{ items.foo.self.foo() }}
+{{ items.foo.is }}
+{{ items.foo.in }}
+{{ items.foo.not }}
+--DATA--
+return array('foo' => 'bar', 'items' => array('foo' => new TwigTestFoo(), 'bar' => 'foo'))
+--CONFIG--
+return array('strict_variables' => false)
+--EXPECT--
+foo
+foo
+bar
+
+bar_a-43
+bar_bar
+foo
+is
+in
+not
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/postfix.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/postfix.test
new file mode 100644 (file)
index 0000000..542c350
--- /dev/null
@@ -0,0 +1,22 @@
+--TEST--
+Twig parses postfix expressions
+--TEMPLATE--
+{% import _self as macros %}
+
+{% macro foo() %}foo{% endmacro %}
+
+{{ 'a' }}
+{{ 'a'|upper }}
+{{ ('a')|upper }}
+{{ -1|upper }}
+{{ macros.foo() }}
+{{ (macros).foo() }}
+--DATA--
+return array();
+--EXPECT--
+a
+A
+A
+-1
+foo
+foo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/strings.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/strings.test
new file mode 100644 (file)
index 0000000..a911661
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+Twig supports string interpolation
+--TEMPLATE--
+{{ "foo #{"foo #{bar} baz"} baz" }}
+{{ "foo #{bar}#{bar} baz" }}
+--DATA--
+return array('bar' => 'BAR');
+--EXPECT--
+foo foo BAR baz baz
+foo BARBAR baz
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/ternary_operator.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/ternary_operator.test
new file mode 100644 (file)
index 0000000..0e6fa96
--- /dev/null
@@ -0,0 +1,18 @@
+--TEST--
+Twig supports the ternary operator
+--TEMPLATE--
+{{ 1 ? 'YES' : 'NO' }}
+{{ 0 ? 'YES' : 'NO' }}
+{{ 0 ? 'YES' : (1 ? 'YES1' : 'NO1') }}
+{{ 0 ? 'YES' : (0 ? 'YES1' : 'NO1') }}
+{{ 1 == 1 ? 'foo<br />':'' }}
+{{ foo ~ (bar ? ('-' ~ bar) : '') }}
+--DATA--
+return array('foo' => 'foo', 'bar' => 'bar')
+--EXPECT--
+YES
+NO
+YES1
+NO1
+foo<br />
+foo-bar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/ternary_operator_noelse.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/ternary_operator_noelse.test
new file mode 100644 (file)
index 0000000..fdc660f
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+Twig supports the ternary operator
+--TEMPLATE--
+{{ 1 ? 'YES' }}
+{{ 0 ? 'YES' }}
+--DATA--
+return array()
+--EXPECT--
+YES
+
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/ternary_operator_nothen.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/ternary_operator_nothen.test
new file mode 100644 (file)
index 0000000..9057e83
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+Twig supports the ternary operator
+--TEMPLATE--
+{{ 'YES' ?: 'NO' }}
+{{ 0 ?: 'NO' }}
+--DATA--
+return array()
+--EXPECT--
+YES
+NO
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/unary.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/unary.test
new file mode 100644 (file)
index 0000000..b79219a
--- /dev/null
@@ -0,0 +1,12 @@
+--TEST--
+Twig supports unary operators (not, -, +)
+--TEMPLATE--
+{{ not 1 }}/{{ not 0 }}
+{{ +1 + 1 }}/{{ -1 - 1 }}
+{{ not (false or true) }}
+--DATA--
+return array()
+--EXPECT--
+/1
+2/-2
+
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/unary_precedence.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/unary_precedence.test
new file mode 100644 (file)
index 0000000..cc6eef8
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+Twig unary operators precedence
+--TEMPLATE--
+{{ -1 - 1 }}
+{{ -1 - -1 }}
+{{ -1 * -1 }}
+{{ 4 / -1 * 5 }}
+--DATA--
+return array()
+--EXPECT--
+-2
+0
+1
+-20
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/abs.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/abs.test
new file mode 100644 (file)
index 0000000..27e93fd
--- /dev/null
@@ -0,0 +1,30 @@
+--TEST--
+"abs" filter
+--TEMPLATE--
+{{ (-5.5)|abs }}
+{{ (-5)|abs }}
+{{ (-0)|abs }}
+{{ 0|abs }}
+{{ 5|abs }}
+{{ 5.5|abs }}
+{{ number1|abs }}
+{{ number2|abs }}
+{{ number3|abs }}
+{{ number4|abs }}
+{{ number5|abs }}
+{{ number6|abs }}
+--DATA--
+return array('number1' => -5.5, 'number2' => -5, 'number3' => -0, 'number4' => 0, 'number5' => 5, 'number6' => 5.5)
+--EXPECT--
+5.5
+5
+0
+0
+5
+5.5
+5.5
+5
+0
+0
+5
+5.5
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch.test
new file mode 100644 (file)
index 0000000..cb6de7f
--- /dev/null
@@ -0,0 +1,31 @@
+--TEST--
+"batch" filter
+--TEMPLATE--
+{% for row in items|batch(3) %}
+  <div class=row>
+  {% for column in row %}
+    <div class=item>{{ column }}</div>
+  {% endfor %}
+  </div>
+{% endfor %}
+--DATA--
+return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'))
+--EXPECT--
+<div class=row>
+      <div class=item>a</div>
+      <div class=item>b</div>
+      <div class=item>c</div>
+    </div>
+  <div class=row>
+      <div class=item>d</div>
+      <div class=item>e</div>
+      <div class=item>f</div>
+    </div>
+  <div class=row>
+      <div class=item>g</div>
+      <div class=item>h</div>
+      <div class=item>i</div>
+    </div>
+  <div class=row>
+      <div class=item>j</div>
+    </div>
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch_float.php b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch_float.php
new file mode 100644 (file)
index 0000000..52de39c
--- /dev/null
@@ -0,0 +1,31 @@
+--TEST--
+"batch" filter
+--TEMPLATE--
+{% for row in items|batch(3.1) %}
+  <div class=row>
+  {% for column in row %}
+    <div class=item>{{ column }}</div>
+  {% endfor %}
+  </div>
+{% endfor %}
+--DATA--
+return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'))
+--EXPECT--
+<div class=row>
+      <div class=item>a</div>
+      <div class=item>b</div>
+      <div class=item>c</div>
+    </div>
+  <div class=row>
+      <div class=item>d</div>
+      <div class=item>e</div>
+      <div class=item>f</div>
+    </div>
+  <div class=row>
+      <div class=item>g</div>
+      <div class=item>h</div>
+      <div class=item>i</div>
+    </div>
+  <div class=row>
+      <div class=item>j</div>
+    </div>
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch_with_empty_fill.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch_with_empty_fill.test
new file mode 100644 (file)
index 0000000..af996f2
--- /dev/null
@@ -0,0 +1,37 @@
+--TEST--
+"batch" filter
+--TEMPLATE--
+<table>
+{% for row in items|batch(3, '') %}
+  <tr>
+  {% for column in row %}
+    <td>{{ column }}</td>
+  {% endfor %}
+  </tr>
+{% endfor %}
+</table>
+--DATA--
+return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'))
+--EXPECT--
+<table>
+  <tr>
+      <td>a</td>
+      <td>b</td>
+      <td>c</td>
+    </tr>
+  <tr>
+      <td>d</td>
+      <td>e</td>
+      <td>f</td>
+    </tr>
+  <tr>
+      <td>g</td>
+      <td>h</td>
+      <td>i</td>
+    </tr>
+  <tr>
+      <td>j</td>
+      <td></td>
+      <td></td>
+    </tr>
+</table>
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch_with_fill.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch_with_fill.test
new file mode 100644 (file)
index 0000000..746295f
--- /dev/null
@@ -0,0 +1,37 @@
+--TEST--
+"batch" filter
+--TEMPLATE--
+<table>
+{% for row in items|batch(3, 'fill') %}
+  <tr>
+  {% for column in row %}
+    <td>{{ column }}</td>
+  {% endfor %}
+  </tr>
+{% endfor %}
+</table>
+--DATA--
+return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'))
+--EXPECT--
+<table>
+  <tr>
+      <td>a</td>
+      <td>b</td>
+      <td>c</td>
+    </tr>
+  <tr>
+      <td>d</td>
+      <td>e</td>
+      <td>f</td>
+    </tr>
+  <tr>
+      <td>g</td>
+      <td>h</td>
+      <td>i</td>
+    </tr>
+  <tr>
+      <td>j</td>
+      <td>fill</td>
+      <td>fill</td>
+    </tr>
+</table>
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/convert_encoding.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/convert_encoding.test
new file mode 100644 (file)
index 0000000..380b04b
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+"convert_encoding" filter
+--CONDITION--
+function_exists('iconv') || function_exists('mb_convert_encoding')
+--TEMPLATE--
+{{ "愛していますか?"|convert_encoding('ISO-2022-JP', 'UTF-8')|convert_encoding('UTF-8', 'ISO-2022-JP') }}
+--DATA--
+return array()
+--EXPECT--
+愛していますか?
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date.test
new file mode 100644 (file)
index 0000000..d40bb04
--- /dev/null
@@ -0,0 +1,76 @@
+--TEST--
+"date" filter
+--TEMPLATE--
+{{ date1|date }}
+{{ date1|date('d/m/Y') }}
+{{ date1|date('d/m/Y H:i:s', 'Asia/Hong_Kong') }}
+{{ date1|date('d/m/Y H:i:s P', 'Asia/Hong_Kong') }}
+{{ date1|date('d/m/Y H:i:s P', 'America/Chicago') }}
+{{ date1|date('e') }}
+{{ date1|date('d/m/Y H:i:s') }}
+
+{{ date2|date }}
+{{ date2|date('d/m/Y') }}
+{{ date2|date('d/m/Y H:i:s', 'Asia/Hong_Kong') }}
+{{ date2|date('d/m/Y H:i:s', timezone1) }}
+{{ date2|date('d/m/Y H:i:s') }}
+
+{{ date3|date }}
+{{ date3|date('d/m/Y') }}
+
+{{ date4|date }}
+{{ date4|date('d/m/Y') }}
+
+{{ date5|date }}
+{{ date5|date('d/m/Y') }}
+
+{{ date6|date('d/m/Y H:i:s P', 'Europe/Paris') }}
+{{ date6|date('d/m/Y H:i:s P', 'Asia/Hong_Kong') }}
+{{ date6|date('d/m/Y H:i:s P', false) }}
+{{ date6|date('e', 'Europe/Paris') }}
+{{ date6|date('e', false) }}
+
+{{ date7|date }}
+--DATA--
+date_default_timezone_set('Europe/Paris');
+return array(
+    'date1' => mktime(13, 45, 0, 10, 4, 2010),
+    'date2' => new DateTime('2010-10-04 13:45'),
+    'date3' => '2010-10-04 13:45',
+    'date4' => 1286199900, // DateTime::createFromFormat('Y-m-d H:i', '2010-10-04 13:45', new DateTimeZone('UTC'))->getTimestamp() -- A unixtimestamp is always GMT
+    'date5' => -189291360, // DateTime::createFromFormat('Y-m-d H:i', '1964-01-02 03:04', new DateTimeZone('UTC'))->getTimestamp(),
+    'date6' => new DateTime('2010-10-04 13:45', new DateTimeZone('America/New_York')),
+    'date7' => '2010-01-28T15:00:00+05:00',
+    'timezone1' => new DateTimeZone('America/New_York'),
+)
+--EXPECT--
+October 4, 2010 13:45
+04/10/2010
+04/10/2010 19:45:00
+04/10/2010 19:45:00 +08:00
+04/10/2010 06:45:00 -05:00
+Europe/Paris
+04/10/2010 13:45:00
+
+October 4, 2010 13:45
+04/10/2010
+04/10/2010 19:45:00
+04/10/2010 07:45:00
+04/10/2010 13:45:00
+
+October 4, 2010 13:45
+04/10/2010
+
+October 4, 2010 15:45
+04/10/2010
+
+January 2, 1964 04:04
+02/01/1964
+
+04/10/2010 19:45:00 +02:00
+05/10/2010 01:45:00 +08:00
+04/10/2010 13:45:00 -04:00
+Europe/Paris
+America/New_York
+
+January 28, 2010 11:00
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format.test
new file mode 100644 (file)
index 0000000..11a1ef4
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+"date" filter
+--TEMPLATE--
+{{ date1|date }}
+{{ date1|date('d/m/Y') }}
+--DATA--
+date_default_timezone_set('UTC');
+$twig->getExtension('core')->setDateFormat('Y-m-d', '%d days %h hours');
+return array(
+    'date1' => mktime(13, 45, 0, 10, 4, 2010),
+)
+--EXPECT--
+2010-10-04
+04/10/2010
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format_interval.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format_interval.test
new file mode 100644 (file)
index 0000000..e6d3707
--- /dev/null
@@ -0,0 +1,16 @@
+--TEST--
+"date" filter (interval support as of PHP 5.3)
+--CONDITION--
+version_compare(phpversion(), '5.3.0', '>=')
+--TEMPLATE--
+{{ date2|date }}
+{{ date2|date('%d days') }}
+--DATA--
+date_default_timezone_set('UTC');
+$twig->getExtension('core')->setDateFormat('Y-m-d', '%d days %h hours');
+return array(
+    'date2' => new DateInterval('P2D'),
+)
+--EXPECT--
+2 days 0 hours
+2 days
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_interval.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_interval.test
new file mode 100644 (file)
index 0000000..0c8c6f1
--- /dev/null
@@ -0,0 +1,19 @@
+--TEST--
+"date" filter (interval support as of PHP 5.3)
+--CONDITION--
+version_compare(phpversion(), '5.3.0', '>=')
+--TEMPLATE--
+{{ date1|date }}
+{{ date1|date('%d days %h hours') }}
+{{ date1|date('%d days %h hours', timezone1) }}
+--DATA--
+date_default_timezone_set('UTC');
+return array(
+    'date1' => new DateInterval('P2D'),
+    // This should have no effect on DateInterval formatting
+    'timezone1' => new DateTimeZone('America/New_York'),
+)
+--EXPECT--
+2 days
+2 days 0 hours
+2 days 0 hours
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_modify.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_modify.test
new file mode 100644 (file)
index 0000000..53d3a69
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+"date_modify" filter
+--TEMPLATE--
+{{ date1|date_modify('-1day')|date('Y-m-d H:i:s') }}
+{{ date2|date_modify('-1day')|date('Y-m-d H:i:s') }}
+--DATA--
+date_default_timezone_set('UTC');
+return array(
+    'date1' => '2010-10-04 13:45',
+    'date2' => new DateTime('2010-10-04 13:45'),
+)
+--EXPECT--
+2010-10-03 13:45:00
+2010-10-03 13:45:00
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_namedargs.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_namedargs.test
new file mode 100644 (file)
index 0000000..4ecde8a
--- /dev/null
@@ -0,0 +1,13 @@
+--TEST--
+"date" filter
+--TEMPLATE--
+{{ date|date(format='d/m/Y H:i:s P', timezone='America/Chicago') }}
+{{ date|date(timezone='America/Chicago', format='d/m/Y H:i:s P') }}
+{{ date|date('d/m/Y H:i:s P', timezone='America/Chicago') }}
+--DATA--
+date_default_timezone_set('UTC');
+return array('date' => mktime(13, 45, 0, 10, 4, 2010))
+--EXPECT--
+04/10/2010 08:45:00 -05:00
+04/10/2010 08:45:00 -05:00
+04/10/2010 08:45:00 -05:00
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/default.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/default.test
new file mode 100644 (file)
index 0000000..0e4404b
--- /dev/null
@@ -0,0 +1,150 @@
+--TEST--
+"default" filter
+--TEMPLATE--
+Variable:
+{{ definedVar                  |default('default') is sameas('default') ? 'ko' : 'ok' }}
+{{ zeroVar                     |default('default') is sameas('default') ? 'ko' : 'ok' }}
+{{ emptyVar                    |default('default') is sameas('default') ? 'ok' : 'ko' }}
+{{ nullVar                     |default('default') is sameas('default') ? 'ok' : 'ko' }}
+{{ undefinedVar                |default('default') is sameas('default') ? 'ok' : 'ko' }}
+Array access:
+{{ nested.definedVar           |default('default') is sameas('default') ? 'ko' : 'ok' }}
+{{ nested['definedVar']        |default('default') is sameas('default') ? 'ko' : 'ok' }}
+{{ nested.zeroVar              |default('default') is sameas('default') ? 'ko' : 'ok' }}
+{{ nested.emptyVar             |default('default') is sameas('default') ? 'ok' : 'ko' }}
+{{ nested.nullVar              |default('default') is sameas('default') ? 'ok' : 'ko' }}
+{{ nested.undefinedVar         |default('default') is sameas('default') ? 'ok' : 'ko' }}
+{{ nested['undefinedVar']      |default('default') is sameas('default') ? 'ok' : 'ko' }}
+{{ undefinedVar.foo            |default('default') is sameas('default') ? 'ok' : 'ko' }}
+Plain values:
+{{ 'defined'                   |default('default') is sameas('default') ? 'ko' : 'ok' }}
+{{ 0                           |default('default') is sameas('default') ? 'ko' : 'ok' }}
+{{ ''                          |default('default') is sameas('default') ? 'ok' : 'ko' }}
+{{ null                        |default('default') is sameas('default') ? 'ok' : 'ko' }}
+Precedence:
+{{ 'o' ~ nullVar               |default('k') }}
+{{ 'o' ~ nested.nullVar        |default('k') }}
+Object methods:
+{{ object.foo                  |default('default') is sameas('default') ? 'ko' : 'ok' }}
+{{ object.undefinedMethod      |default('default') is sameas('default') ? 'ok' : 'ko' }}
+{{ object.getFoo()             |default('default') is sameas('default') ? 'ko' : 'ok' }}
+{{ object.getFoo('a')          |default('default') is sameas('default') ? 'ko' : 'ok' }}
+{{ object.undefinedMethod()    |default('default') is sameas('default') ? 'ok' : 'ko' }}
+{{ object.undefinedMethod('a') |default('default') is sameas('default') ? 'ok' : 'ko' }}
+Deep nested:
+{{ nested.undefinedVar.foo.bar |default('default') is sameas('default') ? 'ok' : 'ko' }}
+{{ nested.definedArray.0       |default('default') is sameas('default') ? 'ko' : 'ok' }}
+{{ nested['definedArray'][0]   |default('default') is sameas('default') ? 'ko' : 'ok' }}
+{{ object.self.foo             |default('default') is sameas('default') ? 'ko' : 'ok' }}
+{{ object.self.undefinedMethod |default('default') is sameas('default') ? 'ok' : 'ko' }}
+{{ object.undefinedMethod.self |default('default') is sameas('default') ? 'ok' : 'ko' }}
+--DATA--
+return array(
+    'definedVar' => 'defined',
+    'zeroVar'    => 0,
+    'emptyVar'   => '',
+    'nullVar'    => null,
+    'nested'     => array(
+        'definedVar'   => 'defined',
+        'zeroVar'      => 0,
+        'emptyVar'     => '',
+        'nullVar'      => null,
+        'definedArray' => array(0),
+    ),
+    'object' => new TwigTestFoo(),
+)
+--CONFIG--
+return array('strict_variables' => false)
+--EXPECT--
+Variable:
+ok
+ok
+ok
+ok
+ok
+Array access:
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+Plain values:
+ok
+ok
+ok
+ok
+Precedence:
+ok
+ok
+Object methods:
+ok
+ok
+ok
+ok
+ok
+ok
+Deep nested:
+ok
+ok
+ok
+ok
+ok
+ok
+--DATA--
+return array(
+    'definedVar' => 'defined',
+    'zeroVar'    => 0,
+    'emptyVar'   => '',
+    'nullVar'    => null,
+    'nested'     => array(
+        'definedVar'   => 'defined',
+        'zeroVar'      => 0,
+        'emptyVar'     => '',
+        'nullVar'      => null,
+        'definedArray' => array(0),
+    ),
+    'object' => new TwigTestFoo(),
+)
+--CONFIG--
+return array('strict_variables' => true)
+--EXPECT--
+Variable:
+ok
+ok
+ok
+ok
+ok
+Array access:
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+Plain values:
+ok
+ok
+ok
+ok
+Precedence:
+ok
+ok
+Object methods:
+ok
+ok
+ok
+ok
+ok
+ok
+Deep nested:
+ok
+ok
+ok
+ok
+ok
+ok
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/dynamic_filter.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/dynamic_filter.test
new file mode 100644 (file)
index 0000000..93c5913
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+dynamic filter
+--TEMPLATE--
+{{ 'bar'|foo_path }}
+{{ 'bar'|a_foo_b_bar }}
+--DATA--
+return array()
+--EXPECT--
+foo/bar
+a/b/bar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape.test
new file mode 100644 (file)
index 0000000..a606c10
--- /dev/null
@@ -0,0 +1,8 @@
+--TEST--
+"escape" filter
+--TEMPLATE--
+{{ "foo <br />"|e }}
+--DATA--
+return array()
+--EXPECT--
+foo &lt;br /&gt;
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape_non_supported_charset.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape_non_supported_charset.test
new file mode 100644 (file)
index 0000000..bba26a0
--- /dev/null
@@ -0,0 +1,8 @@
+--TEST--
+"escape" filter
+--TEMPLATE--
+{{ "愛していますか? <br />"|e }}
+--DATA--
+return array()
+--EXPECT--
+愛していますか? &lt;br /&gt;
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/first.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/first.test
new file mode 100644 (file)
index 0000000..853465b
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+"first" filter
+--TEMPLATE--
+{{ [1, 2, 3, 4]|first }}
+{{ {a: 1, b: 2, c: 3, d: 4}|first }}
+{{ '1234'|first }}
+{{ arr|first }}
+--DATA--
+return array('arr' => new ArrayObject(array(1, 2, 3, 4)))
+--EXPECT--
+1
+1
+1
+1
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/force_escape.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/force_escape.test
new file mode 100644 (file)
index 0000000..85a9b71
--- /dev/null
@@ -0,0 +1,18 @@
+--TEST--
+"escape" filter
+--TEMPLATE--
+{% set foo %}
+    foo<br />
+{% endset %}
+
+{{ foo|e('html') -}}
+{{ foo|e('js') }}
+{% autoescape true %}
+    {{ foo }}
+{% endautoescape %}
+--DATA--
+return array()
+--EXPECT--
+    foo&lt;br /&gt;
+\x20\x20\x20\x20foo\x3Cbr\x20\x2F\x3E\x0A
+        foo<br />
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/format.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/format.test
new file mode 100644 (file)
index 0000000..97221ff
--- /dev/null
@@ -0,0 +1,8 @@
+--TEST--
+"format" filter
+--TEMPLATE--
+{{ string|format(foo, 3) }}
+--DATA--
+return array('string' => '%s/%d', 'foo' => 'bar')
+--EXPECT--
+bar/3
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/join.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/join.test
new file mode 100644 (file)
index 0000000..b342c17
--- /dev/null
@@ -0,0 +1,12 @@
+--TEST--
+"join" filter
+--TEMPLATE--
+{{ ["foo", "bar"]|join(', ') }}
+{{ foo|join(', ') }}
+{{ bar|join(', ') }}
+--DATA--
+return array('foo' => new TwigTestFoo(), 'bar' => new ArrayObject(array(3, 4)))
+--EXPECT--
+foo, bar
+1, 2
+3, 4
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/json_encode.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/json_encode.test
new file mode 100644 (file)
index 0000000..1738d40
--- /dev/null
@@ -0,0 +1,12 @@
+--TEST--
+"json_encode" filter
+--TEMPLATE--
+{{ "foo"|json_encode|raw }}
+{{ foo|json_encode|raw }}
+{{ [foo, "foo"]|json_encode|raw }}
+--DATA--
+return array('foo' => new Twig_Markup('foo', 'UTF-8'))
+--EXPECT--
+"foo"
+"foo"
+["foo","foo"]
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/last.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/last.test
new file mode 100644 (file)
index 0000000..ca3ac0c
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+"last" filter
+--TEMPLATE--
+{{ [1, 2, 3, 4]|last }}
+{{ {a: 1, b: 2, c: 3, d: 4}|last }}
+{{ '1234'|last }}
+{{ arr|last }}
+--DATA--
+return array('arr' => new ArrayObject(array(1, 2, 3, 4)))
+--EXPECT--
+4
+4
+4
+4
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/length.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/length.test
new file mode 100644 (file)
index 0000000..3347474
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+"length" filter
+--TEMPLATE--
+{{ array|length }}
+{{ string|length }}
+{{ number|length }}
+{{ markup|length }}
+--DATA--
+return array('array' => array(1, 4), 'string' => 'foo', 'number' => 1000, 'markup' => new Twig_Markup('foo', 'UTF-8'))
+--EXPECT--
+2
+3
+4
+3
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/length_utf8.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/length_utf8.test
new file mode 100644 (file)
index 0000000..5d5e243
--- /dev/null
@@ -0,0 +1,12 @@
+--TEST--
+"length" filter
+--CONDITION--
+function_exists('mb_get_info')
+--TEMPLATE--
+{{ string|length }}
+{{ markup|length }}
+--DATA--
+return array('string' => 'été', 'markup' => new Twig_Markup('foo', 'UTF-8'))
+--EXPECT--
+3
+3
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/merge.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/merge.test
new file mode 100644 (file)
index 0000000..2bd3d4c
--- /dev/null
@@ -0,0 +1,16 @@
+--TEST--
+"merge" filter
+--TEMPLATE--
+{{ items|merge({'bar': 'foo'})|join }}
+{{ items|merge({'bar': 'foo'})|keys|join }}
+{{ {'bar': 'foo'}|merge(items)|join }}
+{{ {'bar': 'foo'}|merge(items)|keys|join }}
+{{ numerics|merge([4, 5, 6])|join }}
+--DATA--
+return array('items' => array('foo' => 'bar'), 'numerics' => array(1, 2, 3))
+--EXPECT--
+barfoo
+foobar
+foobar
+barfoo
+123456
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/nl2br.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/nl2br.test
new file mode 100644 (file)
index 0000000..6545a9b
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+"nl2br" filter
+--TEMPLATE--
+{{ "I like Twig.\nYou will like it too.\n\nEverybody like it!"|nl2br }}
+{{ text|nl2br }}
+--DATA--
+return array('text' => "If you have some <strong>HTML</strong>\nit will be escaped.")
+--EXPECT--
+I like Twig.<br />
+You will like it too.<br />
+<br />
+Everybody like it!
+If you have some &lt;strong&gt;HTML&lt;/strong&gt;<br />
+it will be escaped.
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/number_format.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/number_format.test
new file mode 100644 (file)
index 0000000..639a865
--- /dev/null
@@ -0,0 +1,18 @@
+--TEST--
+"number_format" filter
+--TEMPLATE--
+{{ 20|number_format }}
+{{ 20.25|number_format }}
+{{ 20.25|number_format(2) }}
+{{ 20.25|number_format(2, ',') }}
+{{ 1020.25|number_format(2, ',') }}
+{{ 1020.25|number_format(2, ',', '.') }}
+--DATA--
+return array();
+--EXPECT--
+20
+20
+20.25
+20,25
+1,020,25
+1.020,25
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/number_format_default.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/number_format_default.test
new file mode 100644 (file)
index 0000000..c6903cc
--- /dev/null
@@ -0,0 +1,21 @@
+--TEST--
+"number_format" filter with defaults.
+--TEMPLATE--
+{{ 20|number_format }}
+{{ 20.25|number_format }}
+{{ 20.25|number_format(1) }}
+{{ 20.25|number_format(2, ',') }}
+{{ 1020.25|number_format }}
+{{ 1020.25|number_format(2, ',') }}
+{{ 1020.25|number_format(2, ',', '.') }}
+--DATA--
+$twig->getExtension('core')->setNumberFormat(2, '!', '=');
+return array();
+--EXPECT--
+20!00
+20!25
+20!3
+20,25
+1=020!25
+1=020,25
+1.020,25
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/replace.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/replace.test
new file mode 100644 (file)
index 0000000..4021660
--- /dev/null
@@ -0,0 +1,8 @@
+--TEST--
+"replace" filter
+--TEMPLATE--
+{{ "I like %this% and %that%."|replace({'%this%': "foo", '%that%': "bar"}) }}
+--DATA--
+return array()
+--EXPECT--
+I like foo and bar.
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/reverse.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/reverse.test
new file mode 100644 (file)
index 0000000..7948ac4
--- /dev/null
@@ -0,0 +1,18 @@
+--TEST--
+"reverse" filter
+--TEMPLATE--
+{{ [1, 2, 3, 4]|reverse|join('') }}
+{{ '1234évènement'|reverse }}
+{{ arr|reverse|join('') }}
+{{ {'a': 'c', 'b': 'a'}|reverse()|join(',') }}
+{{ {'a': 'c', 'b': 'a'}|reverse(preserveKeys=true)|join(glue=',') }}
+{{ {'a': 'c', 'b': 'a'}|reverse(preserve_keys=true)|join(glue=',') }}
+--DATA--
+return array('arr' => new ArrayObject(array(1, 2, 3, 4)))
+--EXPECT--
+4321
+tnemenèvé4321
+4321
+a,c
+a,c
+a,c
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/slice.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/slice.test
new file mode 100644 (file)
index 0000000..b37ad65
--- /dev/null
@@ -0,0 +1,42 @@
+--TEST--
+"slice" filter
+--TEMPLATE--
+{{ [1, 2, 3, 4][1:2]|join('') }}
+{{ {a: 1, b: 2, c: 3, d: 4}[1:2]|join('') }}
+{{ [1, 2, 3, 4][start:length]|join('') }}
+{{ [1, 2, 3, 4]|slice(1, 2)|join('') }}
+{{ [1, 2, 3, 4]|slice(1, 2)|keys|join('') }}
+{{ [1, 2, 3, 4]|slice(1, 2, true)|keys|join('') }}
+{{ {a: 1, b: 2, c: 3, d: 4}|slice(1, 2)|join('') }}
+{{ {a: 1, b: 2, c: 3, d: 4}|slice(1, 2)|keys|join('') }}
+{{ '1234'|slice(1, 2) }}
+{{ '1234'[1:2] }}
+{{ arr|slice(1, 2)|join('') }}
+{{ arr[1:2]|join('') }}
+
+{{ [1, 2, 3, 4]|slice(1)|join('') }}
+{{ [1, 2, 3, 4][1:]|join('') }}
+{{ '1234'|slice(1) }}
+{{ '1234'[1:] }}
+{{ '1234'[:1] }}
+--DATA--
+return array('start' => 1, 'length' => 2, 'arr' => new ArrayObject(array(1, 2, 3, 4)))
+--EXPECT--
+23
+23
+23
+23
+01
+12
+23
+bc
+23
+23
+23
+23
+
+234
+234
+234
+234
+1
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/sort.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/sort.test
new file mode 100644 (file)
index 0000000..21d575f
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+"sort" filter
+--TEMPLATE--
+{{ array1|sort|join }}
+{{ array2|sort|join }}
+--DATA--
+return array('array1' => array(4, 1), 'array2' => array('foo', 'bar'))
+--EXPECT--
+14
+barfoo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/special_chars.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/special_chars.test
new file mode 100644 (file)
index 0000000..dbaf7dc
--- /dev/null
@@ -0,0 +1,8 @@
+--TEST--
+"§" custom filter
+--TEMPLATE--
+{{ 'foo'|§ }}
+--DATA--
+return array()
+--EXPECT--
+§foo§
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/split.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/split.test
new file mode 100644 (file)
index 0000000..ce8ec9c
--- /dev/null
@@ -0,0 +1,18 @@
+--TEST--
+"split" filter
+--TEMPLATE--
+{{ "one,two,three,four,five"|split(',')|join('-') }}
+{{ foo|split(',')|join('-') }}
+{{ foo|split(',', 3)|join('-') }}
+{{ baz|split('')|join('-') }}
+{{ baz|split('', 2)|join('-') }}
+{{ foo|split(',', -2)|join('-') }}
+--DATA--
+return array('foo' => "one,two,three,four,five", 'baz' => '12345',)
+--EXPECT--
+one-two-three-four-five
+one-two-three-four-five
+one-two-three,four,five
+1-2-3-4-5
+12-34-5
+one-two-three
\ No newline at end of file
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/trim.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/trim.test
new file mode 100644 (file)
index 0000000..3192062
--- /dev/null
@@ -0,0 +1,12 @@
+--TEST--
+"trim" filter
+--TEMPLATE--
+{{ "  I like Twig.  "|trim }}
+{{ text|trim }}
+{{ "  foo/"|trim("/") }}
+--DATA--
+return array('text' => "  If you have some <strong>HTML</strong> it will be escaped.  ")
+--EXPECT--
+I like Twig.
+If you have some &lt;strong&gt;HTML&lt;/strong&gt; it will be escaped.
+  foo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/urlencode.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/urlencode.test
new file mode 100644 (file)
index 0000000..de956e7
--- /dev/null
@@ -0,0 +1,12 @@
+--TEST--
+"url_encode" filter
+--TEMPLATE--
+{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode }}
+{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode|raw }}
+{{ {}|url_encode|default("default") }}
+--DATA--
+return array()
+--EXPECT--
+foo=bar&amp;number=3&amp;sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&amp;spa+ce=
+foo=bar&number=3&sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&spa+ce=
+default
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/attribute.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/attribute.test
new file mode 100644 (file)
index 0000000..16ae1e8
--- /dev/null
@@ -0,0 +1,12 @@
+--TEST--
+"attribute" function
+--TEMPLATE--
+{{ attribute(obj, method) }}
+{{ attribute(array, item) }}
+{{ attribute(obj, "bar", ["a", "b"]) }}
+--DATA--
+return array('obj' => new TwigTestFoo(), 'method' => 'foo', 'array' => array('foo' => 'bar'), 'item' => 'foo')
+--EXPECT--
+foo
+bar
+bar_a-b
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/block.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/block.test
new file mode 100644 (file)
index 0000000..8e54059
--- /dev/null
@@ -0,0 +1,12 @@
+--TEST--
+"block" function
+--TEMPLATE--
+{% extends 'base.twig' %}
+{% block bar %}BAR{% endblock %}
+--TEMPLATE(base.twig)--
+{% block foo %}{{ block('bar') }}{% endblock %}
+{% block bar %}BAR_BASE{% endblock %}
+--DATA--
+return array()
+--EXPECT--
+BARBAR
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/constant.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/constant.test
new file mode 100644 (file)
index 0000000..6312879
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+"constant" function
+--TEMPLATE--
+{{ constant('DATE_W3C') == expect ? 'true' : 'false' }}
+{{ constant('ARRAY_AS_PROPS', object) }}
+--DATA--
+return array('expect' => DATE_W3C, 'object' => new ArrayObject(array('hi')));
+--EXPECT--
+true
+2
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/cycle.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/cycle.test
new file mode 100644 (file)
index 0000000..522a63b
--- /dev/null
@@ -0,0 +1,16 @@
+--TEST--
+"cycle" function
+--TEMPLATE--
+{% for i in 0..6 %}
+{{ cycle(array1, i) }}-{{ cycle(array2, i) }}
+{% endfor %}
+--DATA--
+return array('array1' => array('odd', 'even'), 'array2' => array('apple', 'orange', 'citrus'))
+--EXPECT--
+odd-apple
+even-orange
+odd-citrus
+even-apple
+odd-orange
+even-citrus
+odd-apple
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/date.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/date.test
new file mode 100644 (file)
index 0000000..a4c9716
--- /dev/null
@@ -0,0 +1,27 @@
+--TEST--
+"date" function
+--TEMPLATE--
+{{ date() == date('now') ? 'OK' : 'KO' }}
+{{ date() > date('-1day') ? 'OK' : 'KO' }}
+{{ date(date1) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
+{{ date(date2) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
+{{ date(date3) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
+{{ date(date4) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
+{{ date(date5) == date('1964-01-02 03:04') ? 'OK' : 'KO' }}
+--DATA--
+date_default_timezone_set('UTC');
+return array(
+    'date1' => mktime(13, 45, 0, 10, 4, 2010),
+    'date2' => new DateTime('2010-10-04 13:45'),
+    'date3' => '2010-10-04 13:45',
+    'date4' => 1286199900, // DateTime::createFromFormat('Y-m-d H:i', '2010-10-04 13:45', new DateTimeZone('UTC'))->getTimestamp() -- A unixtimestamp is always GMT
+    'date5' => -189291360, // DateTime::createFromFormat('Y-m-d H:i', '1964-01-02 03:04', new DateTimeZone('UTC'))->getTimestamp(),
+)
+--EXPECT--
+OK
+OK
+OK
+OK
+OK
+OK
+OK
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/date_namedargs.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/date_namedargs.test
new file mode 100644 (file)
index 0000000..b9dd9e3
--- /dev/null
@@ -0,0 +1,11 @@
+--TEST--
+"date" function
+--TEMPLATE--
+{{ date(date, "America/New_York")|date('d/m/Y H:i:s P', false) }}
+{{ date(timezone="America/New_York", date=date)|date('d/m/Y H:i:s P', false) }}
+--DATA--
+date_default_timezone_set('UTC');
+return array('date' => mktime(13, 45, 0, 10, 4, 2010))
+--EXPECT--
+04/10/2010 09:45:00 -04:00
+04/10/2010 09:45:00 -04:00
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/dump.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/dump.test
new file mode 100644 (file)
index 0000000..f407237
--- /dev/null
@@ -0,0 +1,16 @@
+--TEST--
+"dump" function
+--CONDITION--
+!extension_loaded('xdebug')
+--TEMPLATE--
+{{ dump('foo') }}
+{{ dump('foo', 'bar') }}
+--DATA--
+return array('foo' => 'foo', 'bar' => 'bar')
+--CONFIG--
+return array('debug' => true, 'autoescape' => false);
+--EXPECT--
+string(3) "foo"
+
+string(3) "foo"
+string(3) "bar"
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/dump_array.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/dump_array.test
new file mode 100644 (file)
index 0000000..889b7a9
--- /dev/null
@@ -0,0 +1,19 @@
+--TEST--
+"dump" function, xdebug is not loaded or xdebug <2.2-dev is loaded
+--CONDITION--
+!extension_loaded('xdebug') || (($r = new ReflectionExtension('xdebug')) && version_compare($r->getVersion(), '2.2-dev', '<'))
+--TEMPLATE--
+{{ dump() }}
+--DATA--
+return array('foo' => 'foo', 'bar' => 'bar')
+--CONFIG--
+return array('debug' => true, 'autoescape' => false);
+--EXPECT--
+array(3) {
+  ["foo"]=>
+  string(3) "foo"
+  ["bar"]=>
+  string(3) "bar"
+  ["global"]=>
+  string(6) "global"
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/dynamic_function.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/dynamic_function.test
new file mode 100644 (file)
index 0000000..913fbc9
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+dynamic function
+--TEMPLATE--
+{{ foo_path('bar') }}
+{{ a_foo_b_bar('bar') }}
+--DATA--
+return array()
+--EXPECT--
+foo/bar
+a/b/bar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/assignment.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/assignment.test
new file mode 100644 (file)
index 0000000..b7653b4
--- /dev/null
@@ -0,0 +1,13 @@
+--TEST--
+"include" function
+--TEMPLATE--
+{% set tmp = include("foo.twig") %}
+
+FOO{{ tmp }}BAR
+--TEMPLATE(foo.twig)--
+FOOBAR
+--DATA--
+return array()
+--EXPECT--
+FOO
+FOOBARBAR
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/autoescaping.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/autoescaping.test
new file mode 100644 (file)
index 0000000..56f8f3b
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+"include" function is safe for auto-escaping
+--TEMPLATE--
+{{ include("foo.twig") }}
+--TEMPLATE(foo.twig)--
+<p>Test</p>
+--DATA--
+return array()
+--EXPECT--
+<p>Test</p>
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/basic.test
new file mode 100644 (file)
index 0000000..a434182
--- /dev/null
@@ -0,0 +1,17 @@
+--TEST--
+"include" function
+--TEMPLATE--
+FOO
+{{ include("foo.twig") }}
+
+BAR
+--TEMPLATE(foo.twig)--
+FOOBAR
+--DATA--
+return array()
+--EXPECT--
+FOO
+
+FOOBAR
+
+BAR
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/expression.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/expression.test
new file mode 100644 (file)
index 0000000..aba30ce
--- /dev/null
@@ -0,0 +1,17 @@
+--TEST--
+"include" function allows expressions for the template to include
+--TEMPLATE--
+FOO
+{{ include(foo) }}
+
+BAR
+--TEMPLATE(foo.twig)--
+FOOBAR
+--DATA--
+return array('foo' => 'foo.twig')
+--EXPECT--
+FOO
+
+FOOBAR
+
+BAR
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/ignore_missing.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/ignore_missing.test
new file mode 100644 (file)
index 0000000..43a2ccc
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+"include" function
+--TEMPLATE--
+{{ include(["foo.twig", "bar.twig"], ignore_missing = true) }}
+{{ include("foo.twig", ignore_missing = true) }}
+{{ include("foo.twig", ignore_missing = true, variables = {}) }}
+{{ include("foo.twig", ignore_missing = true, variables = {}, with_context = true) }}
+--DATA--
+return array()
+--EXPECT--
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/missing.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/missing.test
new file mode 100644 (file)
index 0000000..4d2f6cf
--- /dev/null
@@ -0,0 +1,8 @@
+--TEST--
+"include" function
+--TEMPLATE--
+{{ include("foo.twig") }}
+--DATA--
+return array();
+--EXCEPTION--
+Twig_Error_Loader: Template "foo.twig" is not defined in "index.twig" at line 2.
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/missing_nested.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/missing_nested.test
new file mode 100644 (file)
index 0000000..78fddc7
--- /dev/null
@@ -0,0 +1,16 @@
+--TEST--
+"include" function
+--TEMPLATE--
+{% extends "base.twig" %}
+
+{% block content %}
+    {{ parent() }}
+{% endblock %}
+--TEMPLATE(base.twig)--
+{% block content %}
+    {{ include("foo.twig") }}
+{% endblock %}
+--DATA--
+return array();
+--EXCEPTION--
+Twig_Error_Loader: Template "foo.twig" is not defined in "base.twig" at line 3.
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/sandbox.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/sandbox.test
new file mode 100644 (file)
index 0000000..788a2ab
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+"include" tag sandboxed
+--TEMPLATE--
+{{ include("foo.twig", sandboxed = true) }}
+--TEMPLATE(foo.twig)--
+{{ foo|e }}
+--DATA--
+return array()
+--EXCEPTION--
+Twig_Sandbox_SecurityError: Filter "e" is not allowed in "index.twig" at line 2.
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/template_instance.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/template_instance.test
new file mode 100644 (file)
index 0000000..18d405a
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+"include" function accepts Twig_Template instance
+--TEMPLATE--
+{{ include(foo) }} FOO
+--TEMPLATE(foo.twig)--
+BAR
+--DATA--
+return array('foo' => $twig->loadTemplate('foo.twig'))
+--EXPECT--
+BAR FOO
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/templates_as_array.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/templates_as_array.test
new file mode 100644 (file)
index 0000000..1a81006
--- /dev/null
@@ -0,0 +1,12 @@
+--TEST--
+"include" function
+--TEMPLATE--
+{{ include(["foo.twig", "bar.twig"]) }}
+{{- include(["bar.twig", "foo.twig"]) }}
+--TEMPLATE(foo.twig)--
+foo
+--DATA--
+return array()
+--EXPECT--
+foo
+foo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/with_context.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/with_context.test
new file mode 100644 (file)
index 0000000..35611fb
--- /dev/null
@@ -0,0 +1,16 @@
+--TEST--
+"include" function accept variables and with_context
+--TEMPLATE--
+{{ include("foo.twig") }}
+{{- include("foo.twig", with_context = false) }}
+{{- include("foo.twig", {'foo1': 'bar'}) }}
+{{- include("foo.twig", {'foo1': 'bar'}, with_context = false) }}
+--TEMPLATE(foo.twig)--
+{% for k, v in _context %}{{ k }},{% endfor %}
+--DATA--
+return array('foo' => 'bar')
+--EXPECT--
+foo,global,_parent,
+global,_parent,
+foo,global,foo1,_parent,
+foo1,global,_parent,
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/with_variables.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/with_variables.test
new file mode 100644 (file)
index 0000000..b2ace94
--- /dev/null
@@ -0,0 +1,12 @@
+--TEST--
+"include" function accept variables
+--TEMPLATE--
+{{ include("foo.twig", {'foo': 'bar'}) }}
+{{- include("foo.twig", vars) }}
+--TEMPLATE(foo.twig)--
+{{ foo }}
+--DATA--
+return array('vars' => array('foo' => 'bar'))
+--EXPECT--
+bar
+bar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/range.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/range.test
new file mode 100644 (file)
index 0000000..e0377c8
--- /dev/null
@@ -0,0 +1,8 @@
+--TEST--
+"range" function
+--TEMPLATE--
+{{ range(low=0+1, high=10+0, step=2)|join(',') }}
+--DATA--
+return array()
+--EXPECT--
+1,3,5,7,9
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/special_chars.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/special_chars.test
new file mode 100644 (file)
index 0000000..30c3df5
--- /dev/null
@@ -0,0 +1,8 @@
+--TEST--
+"§" custom function
+--TEMPLATE--
+{{ §('foo') }}
+--DATA--
+return array()
+--EXPECT--
+§foo§
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/template_from_string.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/template_from_string.test
new file mode 100644 (file)
index 0000000..41428da
--- /dev/null
@@ -0,0 +1,11 @@
+--TEST--
+"template_from_string" function
+--TEMPLATE--
+{% include template_from_string(template) %}
+
+{% include template_from_string("Hello {{ name }}") %}
+--DATA--
+return array('name' => 'Fabien', 'template' => "Hello {{ name }}")
+--EXPECT--
+Hello Fabien
+Hello Fabien
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/default_values.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/default_values.test
new file mode 100644 (file)
index 0000000..4ccff7b
--- /dev/null
@@ -0,0 +1,16 @@
+--TEST--
+macro
+--TEMPLATE--
+{% from _self import test %}
+
+{% macro test(a, b = 'bar') -%}
+{{ a }}{{ b }}
+{%- endmacro %}
+
+{{ test('foo') }}
+{{ test('bar', 'foo') }}
+--DATA--
+return array();
+--EXPECT--
+foobar
+barfoo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/nested_calls.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/nested_calls.test
new file mode 100644 (file)
index 0000000..cd25428
--- /dev/null
@@ -0,0 +1,18 @@
+--TEST--
+macro
+--TEMPLATE--
+{% import _self as macros %}
+
+{% macro foo(data) %}
+    {{ data }}
+{% endmacro %}
+
+{% macro bar() %}
+    <br />
+{% endmacro %}
+
+{{ macros.foo(macros.bar()) }}
+--DATA--
+return array();
+--EXPECT--
+<br />
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/reserved_variables.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/reserved_variables.test
new file mode 100644 (file)
index 0000000..cbfb921
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+macro
+--TEMPLATE--
+{% from _self import test %}
+
+{% macro test(this) -%}
+    {{ this }}
+{%- endmacro %}
+
+{{ test(this) }}
+--DATA--
+return array('this' => 'foo');
+--EXPECT--
+foo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/simple.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/simple.test
new file mode 100644 (file)
index 0000000..6a366cd
--- /dev/null
@@ -0,0 +1,22 @@
+--TEST--
+macro
+--TEMPLATE--
+{% import _self as test %}
+{% from _self import test %}
+
+{% macro test(a, b) -%}
+    {{ a|default('a') }}<br />
+    {{- b|default('b') }}<br />
+{%- endmacro %}
+
+{{ test.test() }}
+{{ test() }}
+{{ test.test(1, "c") }}
+{{ test(1, "c") }}
+--DATA--
+return array();
+--EXPECT--
+a<br />b<br />
+a<br />b<br />
+1<br />c<br />
+1<br />c<br />
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/with_filters.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/with_filters.test
new file mode 100644 (file)
index 0000000..685626f
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+macro with a filter
+--TEMPLATE--
+{% import _self as test %}
+
+{% macro test() %}
+    {% filter escape %}foo<br />{% endfilter %}
+{% endmacro %}
+
+{{ test.test() }}
+--DATA--
+return array();
+--EXPECT--
+foo&lt;br /&gt;
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/regression/empty_token.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/regression/empty_token.test
new file mode 100644 (file)
index 0000000..65f6cd2
--- /dev/null
@@ -0,0 +1,8 @@
+--TEST--
+Twig outputs 0 nodes correctly
+--TEMPLATE--
+{{ foo }}0{{ foo }}
+--DATA--
+return array('foo' => 'foo')
+--EXPECT--
+foo0foo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/regression/simple_xml_element.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/regression/simple_xml_element.test
new file mode 100644 (file)
index 0000000..110aef8
--- /dev/null
@@ -0,0 +1,17 @@
+--TEST--
+Twig is able to deal with SimpleXMLElement instances as variables
+--CONDITION--
+version_compare(phpversion(), '5.3.0', '>=')
+--TEMPLATE--
+Hello '{{ images.image.0.group }}'!
+{{ images.children().count() }}
+{% for image in images %}
+    - {{ image.group }}
+{% endfor %}
+--DATA--
+return array('images' => new SimpleXMLElement('<images><image><group>foo</group></image><image><group>bar</group></image></images>'))
+--EXPECT--
+Hello 'foo'!
+2
+    - foo
+    - bar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/regression/strings_like_numbers.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/regression/strings_like_numbers.test
new file mode 100644 (file)
index 0000000..e18e110
--- /dev/null
@@ -0,0 +1,8 @@
+--TEST--
+Twig does not confuse strings with integers in getAttribute()
+--TEMPLATE--
+{{ hash['2e2'] }}
+--DATA--
+return array('hash' => array('2e2' => 'works'))
+--EXPECT--
+works
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/basic.test
new file mode 100644 (file)
index 0000000..2f6a3e1
--- /dev/null
@@ -0,0 +1,26 @@
+--TEST--
+"autoescape" tag applies escaping on its children
+--TEMPLATE--
+{% autoescape %}
+{{ var }}<br />
+{% endautoescape %}
+{% autoescape 'html' %}
+{{ var }}<br />
+{% endautoescape %}
+{% autoescape false %}
+{{ var }}<br />
+{% endautoescape %}
+{% autoescape true %}
+{{ var }}<br />
+{% endautoescape %}
+{% autoescape false %}
+{{ var }}<br />
+{% endautoescape %}
+--DATA--
+return array('var' => '<br />')
+--EXPECT--
+&lt;br /&gt;<br />
+&lt;br /&gt;<br />
+<br /><br />
+&lt;br /&gt;<br />
+<br /><br />
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/blocks.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/blocks.test
new file mode 100644 (file)
index 0000000..05ab83c
--- /dev/null
@@ -0,0 +1,12 @@
+--TEST--
+"autoescape" tag applies escaping on embedded blocks
+--TEMPLATE--
+{% autoescape 'html' %}
+  {% block foo %}
+    {{ var }}
+  {% endblock %}
+{% endautoescape %}
+--DATA--
+return array('var' => '<br />')
+--EXPECT--
+&lt;br /&gt;
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/double_escaping.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/double_escaping.test
new file mode 100644 (file)
index 0000000..9c09724
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+"autoescape" tag does not double-escape
+--TEMPLATE--
+{% autoescape 'html' %}
+{{ var|escape }}
+{% endautoescape %}
+--DATA--
+return array('var' => '<br />')
+--EXPECT--
+&lt;br /&gt;
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/functions.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/functions.test
new file mode 100644 (file)
index 0000000..ce7ea78
--- /dev/null
@@ -0,0 +1,83 @@
+--TEST--
+"autoescape" tag applies escaping after calling functions
+--TEMPLATE--
+
+autoescape false
+{% autoescape false %}
+
+safe_br
+{{ safe_br() }}
+
+unsafe_br
+{{ unsafe_br() }}
+
+{% endautoescape %}
+
+autoescape 'html'
+{% autoescape 'html' %}
+
+safe_br
+{{ safe_br() }}
+
+unsafe_br
+{{ unsafe_br() }}
+
+unsafe_br()|raw
+{{ (unsafe_br())|raw }}
+
+safe_br()|escape
+{{ (safe_br())|escape }}
+
+safe_br()|raw
+{{ (safe_br())|raw }}
+
+unsafe_br()|escape
+{{ (unsafe_br())|escape }}
+
+{% endautoescape %}
+
+autoescape js
+{% autoescape 'js' %}
+
+safe_br
+{{ safe_br() }}
+
+{% endautoescape %}
+--DATA--
+return array()
+--EXPECT--
+
+autoescape false
+
+safe_br
+<br />
+
+unsafe_br
+<br />
+
+
+autoescape 'html'
+
+safe_br
+<br />
+
+unsafe_br
+&lt;br /&gt;
+
+unsafe_br()|raw
+<br />
+
+safe_br()|escape
+&lt;br /&gt;
+
+safe_br()|raw
+<br />
+
+unsafe_br()|escape
+&lt;br /&gt;
+
+
+autoescape js
+
+safe_br
+\x3Cbr\x20\x2F\x3E
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/literal.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/literal.test
new file mode 100644 (file)
index 0000000..e389d4d
--- /dev/null
@@ -0,0 +1,45 @@
+--TEST--
+"autoescape" tag does not apply escaping on literals
+--TEMPLATE--
+{% autoescape 'html' %}
+
+1. Simple literal
+{{ "<br />" }}
+
+2. Conditional expression with only literals
+{{ true ? "<br />" : "<br>" }}
+
+3. Conditional expression with a variable
+{{ true ? "<br />" : someVar }}
+
+4. Nested conditionals with only literals
+{{ true ? (true ? "<br />" : "<br>") : "\n" }}
+
+5. Nested conditionals with a variable
+{{ true ? (true ? "<br />" : someVar) : "\n" }}
+
+6. Nested conditionals with a variable marked safe
+{{ true ? (true ? "<br />" : someVar|raw) : "\n" }}
+
+{% endautoescape %}
+--DATA--
+return array()
+--EXPECT--
+
+1. Simple literal
+<br />
+
+2. Conditional expression with only literals
+<br />
+
+3. Conditional expression with a variable
+&lt;br /&gt;
+
+4. Nested conditionals with only literals
+<br />
+
+5. Nested conditionals with a variable
+&lt;br /&gt;
+
+6. Nested conditionals with a variable marked safe
+<br />
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/nested.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/nested.test
new file mode 100644 (file)
index 0000000..798e6fe
--- /dev/null
@@ -0,0 +1,26 @@
+--TEST--
+"autoescape" tags can be nested at will
+--TEMPLATE--
+{{ var }}
+{% autoescape 'html' %}
+  {{ var }}
+  {% autoescape false %}
+    {{ var }}
+    {% autoescape 'html' %}
+      {{ var }}
+    {% endautoescape %}
+    {{ var }}
+  {% endautoescape %}
+  {{ var }}
+{% endautoescape %}
+{{ var }}
+--DATA--
+return array('var' => '<br />')
+--EXPECT--
+&lt;br /&gt;
+  &lt;br /&gt;
+      <br />
+          &lt;br /&gt;
+        <br />
+    &lt;br /&gt;
+&lt;br /&gt;
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/objects.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/objects.test
new file mode 100644 (file)
index 0000000..e896aa4
--- /dev/null
@@ -0,0 +1,26 @@
+--TEST--
+"autoescape" tag applies escaping to object method calls
+--TEMPLATE--
+{% autoescape 'html' %}
+{{ user.name }}
+{{ user.name|lower }}
+{{ user }}
+{% endautoescape %}
+--DATA--
+class UserForAutoEscapeTest
+{
+  public function getName()
+  {
+    return 'Fabien<br />';
+  }
+
+  public function __toString()
+  {
+     return 'Fabien<br />';
+  }
+}
+return array('user' => new UserForAutoEscapeTest())
+--EXPECT--
+Fabien&lt;br /&gt;
+fabien&lt;br /&gt;
+Fabien&lt;br /&gt;
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/raw.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/raw.test
new file mode 100644 (file)
index 0000000..9f1cedd
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+"autoescape" tag does not escape when raw is used as a filter
+--TEMPLATE--
+{% autoescape 'html' %}
+{{ var|raw }}
+{% endautoescape %}
+--DATA--
+return array('var' => '<br />')
+--EXPECT--
+<br />
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/strategy.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/strategy.test
new file mode 100644 (file)
index 0000000..cf8ccee
--- /dev/null
@@ -0,0 +1,17 @@
+--TEST--
+"autoescape" tag accepts an escaping strategy
+--TEMPLATE--
+{% autoescape true js %}{{ var }}{% endautoescape %}
+
+{% autoescape true html %}{{ var }}{% endautoescape %}
+
+{% autoescape 'js' %}{{ var }}{% endautoescape %}
+
+{% autoescape 'html' %}{{ var }}{% endautoescape %}
+--DATA--
+return array('var' => '<br />"')
+--EXPECT--
+\x3Cbr\x20\x2F\x3E\x22
+&lt;br /&gt;&quot;
+\x3Cbr\x20\x2F\x3E\x22
+&lt;br /&gt;&quot;
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/type.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/type.test
new file mode 100644 (file)
index 0000000..4f41520
--- /dev/null
@@ -0,0 +1,69 @@
+--TEST--
+escape types
+--TEMPLATE--
+
+1. autoescape 'html' |escape('js')
+
+{% autoescape 'html' %}
+<a onclick="alert(&quot;{{ msg|escape('js') }}&quot;)"></a>
+{% endautoescape %}
+
+2. autoescape 'html' |escape('js')
+
+{% autoescape 'html' %}
+<a onclick="alert(&quot;{{ msg|escape('js') }}&quot;)"></a>
+{% endautoescape %}
+
+3. autoescape 'js' |escape('js')
+
+{% autoescape 'js' %}
+<a onclick="alert(&quot;{{ msg|escape('js') }}&quot;)"></a>
+{% endautoescape %}
+
+4. no escape
+
+{% autoescape false %}
+<a onclick="alert(&quot;{{ msg }}&quot;)"></a>
+{% endautoescape %}
+
+5. |escape('js')|escape('html')
+
+{% autoescape false %}
+<a onclick="alert(&quot;{{ msg|escape('js')|escape('html') }}&quot;)"></a>
+{% endautoescape %}
+
+6. autoescape 'html' |escape('js')|escape('html')
+
+{% autoescape 'html' %}
+<a onclick="alert(&quot;{{ msg|escape('js')|escape('html') }}&quot;)"></a>
+{% endautoescape %}
+
+--DATA--
+return array('msg' => "<>\n'\"")
+--EXPECT--
+
+1. autoescape 'html' |escape('js')
+
+<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a>
+
+2. autoescape 'html' |escape('js')
+
+<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a>
+
+3. autoescape 'js' |escape('js')
+
+<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a>
+
+4. no escape
+
+<a onclick="alert(&quot;<>
+'"&quot;)"></a>
+
+5. |escape('js')|escape('html')
+
+<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a>
+
+6. autoescape 'html' |escape('js')|escape('html')
+
+<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a>
+
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_filters.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_filters.test
new file mode 100644 (file)
index 0000000..7821a9a
--- /dev/null
@@ -0,0 +1,131 @@
+--TEST--
+"autoescape" tag applies escaping after calling filters
+--TEMPLATE--
+{% autoescape 'html' %}
+
+(escape_and_nl2br is an escaper filter)
+
+1. Don't escape escaper filter output
+( var is escaped by |escape_and_nl2br, line-breaks are added, 
+  the output is not escaped )
+{{ var|escape_and_nl2br }}
+
+2. Don't escape escaper filter output
+( var is escaped by |escape_and_nl2br, line-breaks are added, 
+  the output is not escaped, |raw is redundant )
+{{ var|escape_and_nl2br|raw }}
+
+3. Explicit escape
+( var is escaped by |escape_and_nl2br, line-breaks are added,
+  the output is explicitly escaped by |escape )
+{{ var|escape_and_nl2br|escape }}
+
+4. Escape non-escaper filter output
+( var is upper-cased by |upper,
+  the output is auto-escaped )
+{{ var|upper }}
+
+5. Escape if last filter is not an escaper
+( var is escaped by |escape_and_nl2br, line-breaks are added,
+  the output is upper-cased by |upper,
+  the output is auto-escaped as |upper is not an escaper )
+{{ var|escape_and_nl2br|upper }}
+
+6. Don't escape escaper filter output
+( var is upper cased by upper,
+  the output is escaped by |escape_and_nl2br, line-breaks are added,
+  the output is not escaped as |escape_and_nl2br is an escaper )
+{{ var|upper|escape_and_nl2br }}
+
+7. Escape if last filter is not an escaper
+( the output of |format is "<b>" ~ var ~ "</b>",
+  the output is auto-escaped )
+{{ "<b>%s</b>"|format(var) }}
+
+8. Escape if last filter is not an escaper
+( the output of |format is "<b>" ~ var ~ "</b>",
+  |raw is redundant,
+  the output is auto-escaped )
+{{ "<b>%s</b>"|raw|format(var) }}
+
+9. Don't escape escaper filter output
+( the output of |format is "<b>" ~ var ~ "</b>",
+  the output is not escaped due to |raw filter at the end )
+{{ "<b>%s</b>"|format(var)|raw }}
+
+10. Don't escape escaper filter output
+( the output of |format is "<b>" ~ var ~ "</b>",
+  the output is not escaped due to |raw filter at the end,
+  the |raw filter on var is redundant )
+{{ "<b>%s</b>"|format(var|raw)|raw }}
+
+{% endautoescape %}
+--DATA--
+return array('var' => "<Fabien>\nTwig")
+--EXPECT--
+
+(escape_and_nl2br is an escaper filter)
+
+1. Don't escape escaper filter output
+( var is escaped by |escape_and_nl2br, line-breaks are added, 
+  the output is not escaped )
+&lt;Fabien&gt;<br />
+Twig
+
+2. Don't escape escaper filter output
+( var is escaped by |escape_and_nl2br, line-breaks are added, 
+  the output is not escaped, |raw is redundant )
+&lt;Fabien&gt;<br />
+Twig
+
+3. Explicit escape
+( var is escaped by |escape_and_nl2br, line-breaks are added,
+  the output is explicitly escaped by |escape )
+&amp;lt;Fabien&amp;gt;&lt;br /&gt;
+Twig
+
+4. Escape non-escaper filter output
+( var is upper-cased by |upper,
+  the output is auto-escaped )
+&lt;FABIEN&gt;
+TWIG
+
+5. Escape if last filter is not an escaper
+( var is escaped by |escape_and_nl2br, line-breaks are added,
+  the output is upper-cased by |upper,
+  the output is auto-escaped as |upper is not an escaper )
+&amp;LT;FABIEN&amp;GT;&lt;BR /&gt;
+TWIG
+
+6. Don't escape escaper filter output
+( var is upper cased by upper,
+  the output is escaped by |escape_and_nl2br, line-breaks are added,
+  the output is not escaped as |escape_and_nl2br is an escaper )
+&lt;FABIEN&gt;<br />
+TWIG
+
+7. Escape if last filter is not an escaper
+( the output of |format is "<b>" ~ var ~ "</b>",
+  the output is auto-escaped )
+&lt;b&gt;&lt;Fabien&gt;
+Twig&lt;/b&gt;
+
+8. Escape if last filter is not an escaper
+( the output of |format is "<b>" ~ var ~ "</b>",
+  |raw is redundant,
+  the output is auto-escaped )
+&lt;b&gt;&lt;Fabien&gt;
+Twig&lt;/b&gt;
+
+9. Don't escape escaper filter output
+( the output of |format is "<b>" ~ var ~ "</b>",
+  the output is not escaped due to |raw filter at the end )
+<b><Fabien>
+Twig</b>
+
+10. Don't escape escaper filter output
+( the output of |format is "<b>" ~ var ~ "</b>",
+  the output is not escaped due to |raw filter at the end,
+  the |raw filter on var is redundant )
+<b><Fabien>
+Twig</b>
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_filters_arguments.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_filters_arguments.test
new file mode 100644 (file)
index 0000000..f58a1e0
--- /dev/null
@@ -0,0 +1,23 @@
+--TEST--
+"autoescape" tag do not applies escaping on filter arguments
+--TEMPLATE--
+{% autoescape 'html' %}
+{{ var|nl2br("<br />") }}
+{{ var|nl2br("<br />"|escape) }}
+{{ var|nl2br(sep) }}
+{{ var|nl2br(sep|raw) }}
+{{ var|nl2br(sep|escape) }}
+{% endautoescape %}
+--DATA--
+return array('var' => "<Fabien>\nTwig", 'sep' => '<br />')
+--EXPECT--
+&lt;Fabien&gt;<br />
+Twig
+&lt;Fabien&gt;&lt;br /&gt;
+Twig
+&lt;Fabien&gt;<br />
+Twig
+&lt;Fabien&gt;<br />
+Twig
+&lt;Fabien&gt;&lt;br /&gt;
+Twig
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_pre_escape_filters.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_pre_escape_filters.test
new file mode 100644 (file)
index 0000000..134c77e
--- /dev/null
@@ -0,0 +1,68 @@
+--TEST--
+"autoescape" tag applies escaping after calling filters, and before calling pre_escape filters
+--TEMPLATE--
+{% autoescape 'html' %}
+
+(nl2br is pre_escaped for "html" and declared safe for "html")
+
+1. Pre-escape and don't post-escape
+( var|escape|nl2br )
+{{ var|nl2br }}
+
+2. Don't double-pre-escape
+( var|escape|nl2br )
+{{ var|escape|nl2br }}
+
+3. Don't escape safe values
+( var|raw|nl2br )
+{{ var|raw|nl2br }}
+
+4. Don't escape safe values
+( var|escape|nl2br|nl2br )
+{{ var|nl2br|nl2br }}
+
+5. Re-escape values that are escaped for an other contexts
+( var|escape_something|escape|nl2br )
+{{ var|escape_something|nl2br }}
+
+6. Still escape when using filters not declared safe
+( var|escape|nl2br|upper|escape )
+{{ var|nl2br|upper }}
+
+{% endautoescape %}
+--DATA--
+return array('var' => "<Fabien>\nTwig")
+--EXPECT--
+
+(nl2br is pre_escaped for "html" and declared safe for "html")
+
+1. Pre-escape and don't post-escape
+( var|escape|nl2br )
+&lt;Fabien&gt;<br />
+Twig
+
+2. Don't double-pre-escape
+( var|escape|nl2br )
+&lt;Fabien&gt;<br />
+Twig
+
+3. Don't escape safe values
+( var|raw|nl2br )
+<Fabien><br />
+Twig
+
+4. Don't escape safe values
+( var|escape|nl2br|nl2br )
+&lt;Fabien&gt;<br /><br />
+Twig
+
+5. Re-escape values that are escaped for an other contexts
+( var|escape_something|escape|nl2br )
+&lt;FABIEN&gt;<br />
+TWIG
+
+6. Still escape when using filters not declared safe
+( var|escape|nl2br|upper|escape )
+&amp;LT;FABIEN&amp;GT;&lt;BR /&gt;
+TWIG
+
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_preserves_safety_filters.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_preserves_safety_filters.test
new file mode 100644 (file)
index 0000000..32d3943
--- /dev/null
@@ -0,0 +1,50 @@
+--TEST--
+"autoescape" tag handles filters preserving the safety
+--TEMPLATE--
+{% autoescape 'html' %}
+
+(preserves_safety is preserving safety for "html")
+
+1. Unsafe values are still unsafe
+( var|preserves_safety|escape )
+{{ var|preserves_safety }}
+
+2. Safe values are still safe
+( var|escape|preserves_safety )
+{{ var|escape|preserves_safety }}
+
+3. Re-escape values that are escaped for an other contexts
+( var|escape_something|preserves_safety|escape )
+{{ var|escape_something|preserves_safety }}
+
+4. Still escape when using filters not declared safe
+( var|escape|preserves_safety|replace({'FABIEN': 'FABPOT'})|escape )
+{{ var|escape|preserves_safety|replace({'FABIEN': 'FABPOT'}) }}
+
+{% endautoescape %}
+--DATA--
+return array('var' => "<Fabien>\nTwig")
+--EXPECT--
+
+(preserves_safety is preserving safety for "html")
+
+1. Unsafe values are still unsafe
+( var|preserves_safety|escape )
+&lt;FABIEN&gt;
+TWIG
+
+2. Safe values are still safe
+( var|escape|preserves_safety )
+&LT;FABIEN&GT;
+TWIG
+
+3. Re-escape values that are escaped for an other contexts
+( var|escape_something|preserves_safety|escape )
+&lt;FABIEN&gt;
+TWIG
+
+4. Still escape when using filters not declared safe
+( var|escape|preserves_safety|replace({'FABIEN': 'FABPOT'})|escape )
+&amp;LT;FABPOT&amp;GT;
+TWIG
+
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/basic.test
new file mode 100644 (file)
index 0000000..360dcf0
--- /dev/null
@@ -0,0 +1,11 @@
+--TEST--
+"block" tag
+--TEMPLATE--
+{% block title1 %}FOO{% endblock %}
+{% block title2 foo|lower %}
+--TEMPLATE(foo.twig)--
+{% block content %}{% endblock %}
+--DATA--
+return array('foo' => 'bar')
+--EXPECT--
+FOObar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/block_unique_name.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/block_unique_name.test
new file mode 100644 (file)
index 0000000..5c205c0
--- /dev/null
@@ -0,0 +1,11 @@
+--TEST--
+"block" tag
+--TEMPLATE--
+{% block content %}
+    {% block content %}
+    {% endblock %}
+{% endblock %}
+--DATA--
+return array()
+--EXCEPTION--
+Twig_Error_Syntax: The block 'content' has already been defined line 2 in "index.twig" at line 3
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/special_chars.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/special_chars.test
new file mode 100644 (file)
index 0000000..be17fed
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+"§" special chars in a block name
+--TEMPLATE--
+{% block § %}
+{% endblock § %}
+--DATA--
+return array()
+--EXPECT--
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/basic.test
new file mode 100644 (file)
index 0000000..f44296e
--- /dev/null
@@ -0,0 +1,35 @@
+--TEST--
+"embed" tag
+--TEMPLATE--
+FOO
+{% embed "foo.twig" %}
+    {% block c1 %}
+        {{ parent() }}
+        block1extended
+    {% endblock %}
+{% endembed %}
+
+BAR
+--TEMPLATE(foo.twig)--
+A
+{% block c1 %}
+    block1
+{% endblock %}
+B
+{% block c2 %}
+    block2
+{% endblock %}
+C
+--DATA--
+return array()
+--EXPECT--
+FOO
+
+A
+            block1
+
+        block1extended
+    B
+    block2
+C
+BAR
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/error_line.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/error_line.test
new file mode 100644 (file)
index 0000000..71ab2e0
--- /dev/null
@@ -0,0 +1,16 @@
+--TEST--
+"embed" tag
+--TEMPLATE(index.twig)--
+FOO
+{% embed "foo.twig" %}
+    {% block c1 %}
+        {{ nothing }}
+    {% endblock %}
+{% endembed %}
+BAR
+--TEMPLATE(foo.twig)--
+{% block c1 %}{% endblock %}
+--DATA--
+return array()
+--EXCEPTION--
+Twig_Error_Runtime: Variable "nothing" does not exist in "index.twig" at line 5
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/multiple.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/multiple.test
new file mode 100644 (file)
index 0000000..da161e6
--- /dev/null
@@ -0,0 +1,50 @@
+--TEST--
+"embed" tag
+--TEMPLATE--
+FOO
+{% embed "foo.twig" %}
+    {% block c1 %}
+        {{ parent() }}
+        block1extended
+    {% endblock %}
+{% endembed %}
+
+{% embed "foo.twig" %}
+    {% block c1 %}
+        {{ parent() }}
+        block1extended
+    {% endblock %}
+{% endembed %}
+
+BAR
+--TEMPLATE(foo.twig)--
+A
+{% block c1 %}
+    block1
+{% endblock %}
+B
+{% block c2 %}
+    block2
+{% endblock %}
+C
+--DATA--
+return array()
+--EXPECT--
+FOO
+
+A
+            block1
+
+        block1extended
+    B
+    block2
+C
+
+A
+            block1
+
+        block1extended
+    B
+    block2
+C
+BAR
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/nested.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/nested.test
new file mode 100644 (file)
index 0000000..81563dc
--- /dev/null
@@ -0,0 +1,42 @@
+--TEST--
+"embed" tag
+--TEMPLATE--
+{% embed "foo.twig" %}
+    {% block c1 %}
+        {{ parent() }}
+        {% embed "foo.twig" %}
+            {% block c1 %}
+                {{ parent() }}
+                block1extended
+            {% endblock %}
+        {% endembed %}
+
+    {% endblock %}
+{% endembed %}
+--TEMPLATE(foo.twig)--
+A
+{% block c1 %}
+    block1
+{% endblock %}
+B
+{% block c2 %}
+    block2
+{% endblock %}
+C
+--DATA--
+return array()
+--EXPECT--
+A
+            block1
+
+        
+A
+                    block1
+
+                block1extended
+            B
+    block2
+C
+    B
+    block2
+C
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/with_extends.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/with_extends.test
new file mode 100644 (file)
index 0000000..cf7953d
--- /dev/null
@@ -0,0 +1,57 @@
+--TEST--
+"embed" tag
+--TEMPLATE--
+{% extends "base.twig" %}
+
+{% block c1 %}
+    {{ parent() }}
+    blockc1baseextended
+{% endblock %}
+
+{% block c2 %}
+    {{ parent() }}
+
+    {% embed "foo.twig" %}
+        {% block c1 %}
+            {{ parent() }}
+            block1extended
+        {% endblock %}
+    {% endembed %}
+{% endblock %}
+--TEMPLATE(base.twig)--
+A
+{% block c1 %}
+    blockc1base
+{% endblock %}
+{% block c2 %}
+    blockc2base
+{% endblock %}
+B
+--TEMPLATE(foo.twig)--
+A
+{% block c1 %}
+    block1
+{% endblock %}
+B
+{% block c2 %}
+    block2
+{% endblock %}
+C
+--DATA--
+return array()
+--EXPECT--
+A
+        blockc1base
+
+    blockc1baseextended
+        blockc2base
+
+
+    
+A
+                block1
+
+            block1extended
+        B
+    block2
+CB
\ No newline at end of file
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/basic.test
new file mode 100644 (file)
index 0000000..82094f2
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+"filter" tag applies a filter on its children
+--TEMPLATE--
+{% filter upper %}
+Some text with a {{ var }}
+{% endfilter %}
+--DATA--
+return array('var' => 'var')
+--EXPECT--
+SOME TEXT WITH A VAR
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/json_encode.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/json_encode.test
new file mode 100644 (file)
index 0000000..3e7148b
--- /dev/null
@@ -0,0 +1,8 @@
+--TEST--
+"filter" tag applies a filter on its children
+--TEMPLATE--
+{% filter json_encode|raw %}test{% endfilter %}
+--DATA--
+return array()
+--EXPECT--
+"test"
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/multiple.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/multiple.test
new file mode 100644 (file)
index 0000000..75512ef
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+"filter" tags accept multiple chained filters
+--TEMPLATE--
+{% filter lower|title %}
+  {{ var }}
+{% endfilter %}
+--DATA--
+return array('var' => 'VAR')
+--EXPECT--
+    Var
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/nested.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/nested.test
new file mode 100644 (file)
index 0000000..7e4e4eb
--- /dev/null
@@ -0,0 +1,16 @@
+--TEST--
+"filter" tags can be nested at will
+--TEMPLATE--
+{% filter lower|title %}
+  {{ var }}
+  {% filter upper %}
+    {{ var }}
+  {% endfilter %}
+  {{ var }}
+{% endfilter %}
+--DATA--
+return array('var' => 'var')
+--EXPECT--
+  Var
+      Var
+    Var
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/with_for_tag.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/with_for_tag.test
new file mode 100644 (file)
index 0000000..22745ea
--- /dev/null
@@ -0,0 +1,13 @@
+--TEST--
+"filter" tag applies the filter on "for" tags
+--TEMPLATE--
+{% filter upper %}
+{% for item in items %}
+{{ item }}
+{% endfor %}
+{% endfilter %}
+--DATA--
+return array('items' => array('a', 'b'))
+--EXPECT--
+A
+B
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/with_if_tag.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/with_if_tag.test
new file mode 100644 (file)
index 0000000..afd95b2
--- /dev/null
@@ -0,0 +1,29 @@
+--TEST--
+"filter" tag applies the filter on "if" tags
+--TEMPLATE--
+{% filter upper %}
+{% if items %}
+{{ items|join(', ') }}
+{% endif %}
+
+{% if items.3 is defined %}
+FOO
+{% else %}
+{{ items.1 }}
+{% endif %}
+
+{% if items.3 is defined %}
+FOO
+{% elseif items.1 %}
+{{ items.0 }}
+{% endif %}
+
+{% endfilter %}
+--DATA--
+return array('items' => array('a', 'b'))
+--EXPECT--
+A, B
+
+B
+
+A
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/condition.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/condition.test
new file mode 100644 (file)
index 0000000..380531f
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+"for" tag takes a condition
+--TEMPLATE--
+{% for i in 1..5 if i is odd -%}
+    {{ loop.index }}.{{ i }}{{ foo.bar }}
+{% endfor %}
+--DATA--
+return array('foo' => array('bar' => 'X'))
+--CONFIG--
+return array('strict_variables' => false)
+--EXPECT--
+1.1X
+2.3X
+3.5X
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/context.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/context.test
new file mode 100644 (file)
index 0000000..ddc6930
--- /dev/null
@@ -0,0 +1,18 @@
+--TEST--
+"for" tag keeps the context safe
+--TEMPLATE--
+{% for item in items %}
+  {% for item in items %}
+    * {{ item }}
+  {% endfor %}
+  * {{ item }}
+{% endfor %}
+--DATA--
+return array('items' => array('a', 'b'))
+--EXPECT--
+      * a
+      * b
+    * a
+      * a
+      * b
+    * b
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/else.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/else.test
new file mode 100644 (file)
index 0000000..20ccc88
--- /dev/null
@@ -0,0 +1,23 @@
+--TEST--
+"for" tag can use an "else" clause
+--TEMPLATE--
+{% for item in items %}
+  * {{ item }}
+{% else %}
+  no item
+{% endfor %}
+--DATA--
+return array('items' => array('a', 'b'))
+--EXPECT--
+  * a
+  * b
+--DATA--
+return array('items' => array())
+--EXPECT--
+  no item
+--DATA--
+return array()
+--CONFIG--
+return array('strict_variables' => false)
+--EXPECT--
+  no item
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/inner_variables.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/inner_variables.test
new file mode 100644 (file)
index 0000000..49fb9ca
--- /dev/null
@@ -0,0 +1,17 @@
+--TEST--
+"for" tag does not reset inner variables
+--TEMPLATE--
+{% for i in 1..2 %}
+  {% for j in 0..2 %}
+    {{k}}{% set k = k+1 %} {{ loop.parent.loop.index }}
+  {% endfor %}
+{% endfor %}
+--DATA--
+return array('k' => 0)
+--EXPECT--
+      0 1
+      1 1
+      2 1
+        3 2
+      4 2
+      5 2
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/keys.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/keys.test
new file mode 100644 (file)
index 0000000..4e22cb4
--- /dev/null
@@ -0,0 +1,11 @@
+--TEST--
+"for" tag can iterate over keys
+--TEMPLATE--
+{% for key in items|keys %}
+  * {{ key }}
+{% endfor %}
+--DATA--
+return array('items' => array('a', 'b'))
+--EXPECT--
+  * 0
+  * 1
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/keys_and_values.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/keys_and_values.test
new file mode 100644 (file)
index 0000000..4c21168
--- /dev/null
@@ -0,0 +1,11 @@
+--TEST--
+"for" tag can iterate over keys and values
+--TEMPLATE--
+{% for key, item in items %}
+  * {{ key }}/{{ item }}
+{% endfor %}
+--DATA--
+return array('items' => array('a', 'b'))
+--EXPECT--
+  * 0/a
+  * 1/b
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_context.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_context.test
new file mode 100644 (file)
index 0000000..93bc76a
--- /dev/null
@@ -0,0 +1,19 @@
+--TEST--
+"for" tag adds a loop variable to the context
+--TEMPLATE--
+{% for item in items %}
+  * {{ loop.index }}/{{ loop.index0 }}
+  * {{ loop.revindex }}/{{ loop.revindex0 }}
+  * {{ loop.first }}/{{ loop.last }}/{{ loop.length }}
+
+{% endfor %}
+--DATA--
+return array('items' => array('a', 'b'))
+--EXPECT--
+  * 1/0
+  * 2/1
+  * 1//2
+
+  * 2/1
+  * 1/0
+  * /1/2
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_context_local.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_context_local.test
new file mode 100644 (file)
index 0000000..58af2c3
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+"for" tag adds a loop variable to the context locally
+--TEMPLATE--
+{% for item in items %}
+{% endfor %}
+{% if loop is not defined %}WORKS{% endif %}
+--DATA--
+return array('items' => array())
+--EXPECT--
+WORKS
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_not_defined.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_not_defined.test
new file mode 100644 (file)
index 0000000..4301ef2
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+"for" tag
+--TEMPLATE--
+{% for i, item in items if i > 0 %}
+    {{ loop.last }}
+{% endfor %}
+--DATA--
+return array('items' => array('a', 'b'))
+--EXCEPTION--
+Twig_Error_Syntax: The "loop.last" variable is not defined when looping with a condition in "index.twig" at line 3
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_not_defined_cond.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_not_defined_cond.test
new file mode 100644 (file)
index 0000000..c7e723a
--- /dev/null
@@ -0,0 +1,9 @@
+--TEST--
+"for" tag
+--TEMPLATE--
+{% for i, item in items if loop.last > 0 %}
+{% endfor %}
+--DATA--
+return array('items' => array('a', 'b'))
+--EXCEPTION--
+Twig_Error_Syntax: The "loop" variable cannot be used in a looping condition in "index.twig" at line 2
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/nested_else.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/nested_else.test
new file mode 100644 (file)
index 0000000..f8b9f6b
--- /dev/null
@@ -0,0 +1,17 @@
+--TEST--
+"for" tag can use an "else" clause
+--TEMPLATE--
+{% for item in items %}
+  {% for item in items1 %}
+    * {{ item }}
+  {% else %}
+    no {{ item }}
+  {% endfor %}
+{% else %}
+  no item1
+{% endfor %}
+--DATA--
+return array('items' => array('a', 'b'), 'items1' => array())
+--EXPECT--
+no a
+        no b
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/objects.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/objects.test
new file mode 100644 (file)
index 0000000..5034437
--- /dev/null
@@ -0,0 +1,43 @@
+--TEST--
+"for" tag iterates over iterable objects
+--TEMPLATE--
+{% for item in items %}
+  * {{ item }}
+  * {{ loop.index }}/{{ loop.index0 }}
+  * {{ loop.first }}
+
+{% endfor %}
+
+{% for key, value in items %}
+  * {{ key }}/{{ value }}
+{% endfor %}
+
+{% for key in items|keys %}
+  * {{ key }}
+{% endfor %}
+--DATA--
+class ItemsIterator implements Iterator
+{
+  protected $values = array('foo' => 'bar', 'bar' => 'foo');
+  public function current() { return current($this->values); }
+  public function key() { return key($this->values); }
+  public function next() { return next($this->values); }
+  public function rewind() { return reset($this->values); }
+  public function valid() { return false !== current($this->values); }
+}
+return array('items' => new ItemsIterator())
+--EXPECT--
+  * bar
+  * 1/0
+  * 1
+
+  * foo
+  * 2/1
+  * 
+
+
+  * foo/bar
+  * bar/foo
+
+  * foo
+  * bar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/objects_countable.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/objects_countable.test
new file mode 100644 (file)
index 0000000..4a1ff61
--- /dev/null
@@ -0,0 +1,47 @@
+--TEST--
+"for" tag iterates over iterable and countable objects
+--TEMPLATE--
+{% for item in items %}
+  * {{ item }}
+  * {{ loop.index }}/{{ loop.index0 }}
+  * {{ loop.revindex }}/{{ loop.revindex0 }}
+  * {{ loop.first }}/{{ loop.last }}/{{ loop.length }}
+
+{% endfor %}
+
+{% for key, value in items %}
+  * {{ key }}/{{ value }}
+{% endfor %}
+
+{% for key in items|keys %}
+  * {{ key }}
+{% endfor %}
+--DATA--
+class ItemsIteratorCountable implements Iterator, Countable
+{
+  protected $values = array('foo' => 'bar', 'bar' => 'foo');
+  public function current() { return current($this->values); }
+  public function key() { return key($this->values); }
+  public function next() { return next($this->values); }
+  public function rewind() { return reset($this->values); }
+  public function valid() { return false !== current($this->values); }
+  public function count() { return count($this->values); }
+}
+return array('items' => new ItemsIteratorCountable())
+--EXPECT--
+  * bar
+  * 1/0
+  * 2/1
+  * 1//2
+
+  * foo
+  * 2/1
+  * 1/0
+  * /1/2
+
+
+  * foo/bar
+  * bar/foo
+
+  * foo
+  * bar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/recursive.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/recursive.test
new file mode 100644 (file)
index 0000000..17b2e22
--- /dev/null
@@ -0,0 +1,18 @@
+--TEST--
+"for" tags can be nested
+--TEMPLATE--
+{% for key, item in items %}
+* {{ key }} ({{ loop.length }}):
+{% for value in item %}
+  * {{ value }} ({{ loop.length }})
+{% endfor %}
+{% endfor %}
+--DATA--
+return array('items' => array('a' => array('a1', 'a2', 'a3'), 'b' => array('b1')))
+--EXPECT--
+* a (2):
+  * a1 (3)
+  * a2 (3)
+  * a3 (3)
+* b (2):
+  * b1 (1)
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/values.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/values.test
new file mode 100644 (file)
index 0000000..82f2ae8
--- /dev/null
@@ -0,0 +1,11 @@
+--TEST--
+"for" tag iterates over item values
+--TEMPLATE--
+{% for item in items %}
+  * {{ item }}
+{% endfor %}
+--DATA--
+return array('items' => array('a', 'b'))
+--EXPECT--
+  * a
+  * b
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/from.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/from.test
new file mode 100644 (file)
index 0000000..5f5da0e
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+global variables
+--TEMPLATE--
+{% include "included.twig" %}
+{% from "included.twig" import foobar %}
+{{ foobar() }}
+--TEMPLATE(included.twig)--
+{% macro foobar() %}
+called foobar
+{% endmacro %}
+--DATA--
+return array();
+--EXPECT--
+called foobar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/if/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/if/basic.test
new file mode 100644 (file)
index 0000000..c1c3d27
--- /dev/null
@@ -0,0 +1,22 @@
+--TEST--
+"if" creates a condition
+--TEMPLATE--
+{% if a is defined %}
+  {{ a }}
+{% elseif b is defined %}
+  {{ b }}
+{% else %}
+  NOTHING
+{% endif %}
+--DATA--
+return array('a' => 'a')
+--EXPECT--
+  a
+--DATA--
+return array('b' => 'b')
+--EXPECT--
+  b
+--DATA--
+return array()
+--EXPECT--
+  NOTHING
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/if/expression.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/if/expression.test
new file mode 100644 (file)
index 0000000..edfb73d
--- /dev/null
@@ -0,0 +1,22 @@
+--TEST--
+"if" takes an expression as a test
+--TEMPLATE--
+{% if a < 2 %}
+  A1
+{% elseif a > 10 %}
+  A2
+{% else %}
+  A3
+{% endif %}
+--DATA--
+return array('a' => 1)
+--EXPECT--
+  A1
+--DATA--
+return array('a' => 12)
+--EXPECT--
+  A2
+--DATA--
+return array('a' => 7)
+--EXPECT--
+  A3
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/basic.test
new file mode 100644 (file)
index 0000000..8fe1a6c
--- /dev/null
@@ -0,0 +1,16 @@
+--TEST--
+"include" tag
+--TEMPLATE--
+FOO
+{% include "foo.twig" %}
+
+BAR
+--TEMPLATE(foo.twig)--
+FOOBAR
+--DATA--
+return array()
+--EXPECT--
+FOO
+
+FOOBAR
+BAR
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/expression.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/expression.test
new file mode 100644 (file)
index 0000000..eaeeb11
--- /dev/null
@@ -0,0 +1,16 @@
+--TEST--
+"include" tag allows expressions for the template to include
+--TEMPLATE--
+FOO
+{% include foo %}
+
+BAR
+--TEMPLATE(foo.twig)--
+FOOBAR
+--DATA--
+return array('foo' => 'foo.twig')
+--EXPECT--
+FOO
+
+FOOBAR
+BAR
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/ignore_missing.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/ignore_missing.test
new file mode 100644 (file)
index 0000000..24aed06
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+"include" tag
+--TEMPLATE--
+{% include ["foo.twig", "bar.twig"] ignore missing %}
+{% include "foo.twig" ignore missing %}
+{% include "foo.twig" ignore missing with {} %}
+{% include "foo.twig" ignore missing with {} only %}
+--DATA--
+return array()
+--EXPECT--
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/missing.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/missing.test
new file mode 100644 (file)
index 0000000..f25e871
--- /dev/null
@@ -0,0 +1,8 @@
+--TEST--
+"include" tag
+--TEMPLATE--
+{% include "foo.twig" %}
+--DATA--
+return array();
+--EXCEPTION--
+Twig_Error_Loader: Template "foo.twig" is not defined in "index.twig" at line 2.
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/missing_nested.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/missing_nested.test
new file mode 100644 (file)
index 0000000..86c1864
--- /dev/null
@@ -0,0 +1,16 @@
+--TEST--
+"include" tag
+--TEMPLATE--
+{% extends "base.twig" %}
+
+{% block content %}
+    {{ parent() }}
+{% endblock %}
+--TEMPLATE(base.twig)--
+{% block content %}
+    {% include "foo.twig" %}
+{% endblock %}
+--DATA--
+return array();
+--EXCEPTION--
+Twig_Error_Loader: Template "foo.twig" is not defined in "base.twig" at line 3.
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/only.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/only.test
new file mode 100644 (file)
index 0000000..77760a0
--- /dev/null
@@ -0,0 +1,16 @@
+--TEST--
+"include" tag accept variables and only
+--TEMPLATE--
+{% include "foo.twig" %}
+{% include "foo.twig" only %}
+{% include "foo.twig" with {'foo1': 'bar'} %}
+{% include "foo.twig" with {'foo1': 'bar'} only %}
+--TEMPLATE(foo.twig)--
+{% for k, v in _context %}{{ k }},{% endfor %}
+--DATA--
+return array('foo' => 'bar')
+--EXPECT--
+foo,global,_parent,
+global,_parent,
+foo,global,foo1,_parent,
+foo1,global,_parent,
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/template_instance.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/template_instance.test
new file mode 100644 (file)
index 0000000..6ba064a
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+"include" tag accepts Twig_Template instance
+--TEMPLATE--
+{% include foo %} FOO
+--TEMPLATE(foo.twig)--
+BAR
+--DATA--
+return array('foo' => $twig->loadTemplate('foo.twig'))
+--EXPECT--
+BAR FOO
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/templates_as_array.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/templates_as_array.test
new file mode 100644 (file)
index 0000000..ab670ee
--- /dev/null
@@ -0,0 +1,12 @@
+--TEST--
+"include" tag
+--TEMPLATE--
+{% include ["foo.twig", "bar.twig"] %}
+{% include ["bar.twig", "foo.twig"] %}
+--TEMPLATE(foo.twig)--
+foo
+--DATA--
+return array()
+--EXPECT--
+foo
+foo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/with_variables.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/with_variables.test
new file mode 100644 (file)
index 0000000..41384ac
--- /dev/null
@@ -0,0 +1,12 @@
+--TEST--
+"include" tag accept variables
+--TEMPLATE--
+{% include "foo.twig" with {'foo': 'bar'} %}
+{% include "foo.twig" with vars %}
+--TEMPLATE(foo.twig)--
+{{ foo }}
+--DATA--
+return array('vars' => array('foo' => 'bar'))
+--EXPECT--
+bar
+bar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/basic.test
new file mode 100644 (file)
index 0000000..0778a4b
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+"extends" tag
+--TEMPLATE--
+{% extends "foo.twig" %}
+
+{% block content %}
+FOO
+{% endblock %}
+--TEMPLATE(foo.twig)--
+{% block content %}{% endblock %}
+--DATA--
+return array()
+--EXPECT--
+FOO
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/conditional.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/conditional.test
new file mode 100644 (file)
index 0000000..8576e77
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+"extends" tag
+--TEMPLATE--
+{% extends standalone ? foo : 'bar.twig' %}
+
+{% block content %}{{ parent() }}FOO{% endblock %}
+--TEMPLATE(foo.twig)--
+{% block content %}FOO{% endblock %}
+--TEMPLATE(bar.twig)--
+{% block content %}BAR{% endblock %}
+--DATA--
+return array('foo' => 'foo.twig', 'standalone' => true)
+--EXPECT--
+FOOFOO
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/dynamic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/dynamic.test
new file mode 100644 (file)
index 0000000..ee06ddc
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+"extends" tag
+--TEMPLATE--
+{% extends foo %}
+
+{% block content %}
+FOO
+{% endblock %}
+--TEMPLATE(foo.twig)--
+{% block content %}{% endblock %}
+--DATA--
+return array('foo' => 'foo.twig')
+--EXPECT--
+FOO
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/empty.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/empty.test
new file mode 100644 (file)
index 0000000..784f357
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+"extends" tag
+--TEMPLATE--
+{% extends "foo.twig" %}
+--TEMPLATE(foo.twig)--
+{% block content %}FOO{% endblock %}
+--DATA--
+return array()
+--EXPECT--
+FOO
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/extends_as_array.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/extends_as_array.test
new file mode 100644 (file)
index 0000000..a1cb1ce
--- /dev/null
@@ -0,0 +1,12 @@
+--TEST--
+"extends" tag
+--TEMPLATE--
+{% extends ["foo.twig", "bar.twig"] %}
+--TEMPLATE(bar.twig)--
+{% block content %}
+foo
+{% endblock %}
+--DATA--
+return array()
+--EXPECT--
+foo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/multiple.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/multiple.test
new file mode 100644 (file)
index 0000000..dfc2b6c
--- /dev/null
@@ -0,0 +1,12 @@
+--TEST--
+"extends" tag
+--TEMPLATE--
+{% extends "layout.twig" %}{% block content %}{{ parent() }}index {% endblock %}
+--TEMPLATE(layout.twig)--
+{% extends "base.twig" %}{% block content %}{{ parent() }}layout {% endblock %}
+--TEMPLATE(base.twig)--
+{% block content %}base {% endblock %}
+--DATA--
+return array()
+--EXPECT--
+base layout index
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/nested_blocks.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/nested_blocks.test
new file mode 100644 (file)
index 0000000..faca925
--- /dev/null
@@ -0,0 +1,22 @@
+--TEST--
+"block" tag
+--TEMPLATE--
+{% extends "foo.twig" %}
+
+{% block content %}
+    {% block subcontent %}
+        {% block subsubcontent %}
+            SUBSUBCONTENT
+        {% endblock %}
+    {% endblock %}
+{% endblock %}
+--TEMPLATE(foo.twig)--
+{% block content %}
+    {% block subcontent %}
+        SUBCONTENT
+    {% endblock %}
+{% endblock %}
+--DATA--
+return array()
+--EXPECT--
+SUBSUBCONTENT
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/nested_blocks_parent_only.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/nested_blocks_parent_only.test
new file mode 100644 (file)
index 0000000..0ad11d0
--- /dev/null
@@ -0,0 +1,15 @@
+--TEST--
+"block" tag
+--TEMPLATE--
+{% block content %}
+    CONTENT
+    {%- block subcontent -%}
+        SUBCONTENT
+    {%- endblock -%}
+    ENDCONTENT
+{% endblock %}
+--TEMPLATE(foo.twig)--
+--DATA--
+return array()
+--EXPECT--
+CONTENTSUBCONTENTENDCONTENT
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/nested_inheritance.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/nested_inheritance.test
new file mode 100644 (file)
index 0000000..71e3cdf
--- /dev/null
@@ -0,0 +1,16 @@
+--TEST--
+"extends" tag
+--TEMPLATE--
+{% extends "layout.twig" %}
+{% block inside %}INSIDE{% endblock inside %}
+--TEMPLATE(layout.twig)--
+{% extends "base.twig" %}
+{% block body %}
+    {% block inside '' %}
+{% endblock body %}
+--TEMPLATE(base.twig)--
+{% block body '' %}
+--DATA--
+return array()
+--EXPECT--
+INSIDE
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent.test
new file mode 100644 (file)
index 0000000..4f975db
--- /dev/null
@@ -0,0 +1,12 @@
+--TEST--
+"extends" tag
+--TEMPLATE--
+{% extends "foo.twig" %}
+
+{% block content %}{{ parent() }}FOO{{ parent() }}{% endblock %}
+--TEMPLATE(foo.twig)--
+{% block content %}BAR{% endblock %}
+--DATA--
+return array()
+--EXPECT--
+BARFOOBAR
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_change.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_change.test
new file mode 100644 (file)
index 0000000..a8bc90c
--- /dev/null
@@ -0,0 +1,16 @@
+--TEST--
+"extends" tag
+--TEMPLATE--
+{% extends foo ? 'foo.twig' : 'bar.twig' %}
+--TEMPLATE(foo.twig)--
+FOO
+--TEMPLATE(bar.twig)--
+BAR
+--DATA--
+return array('foo' => true)
+--EXPECT--
+FOO
+--DATA--
+return array('foo' => false)
+--EXPECT--
+BAR
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_in_a_block.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_in_a_block.test
new file mode 100644 (file)
index 0000000..c9e86b1
--- /dev/null
@@ -0,0 +1,8 @@
+--TEST--
+"extends" tag
+--TEMPLATE--
+{% block content %}
+    {% extends "foo.twig" %}
+{% endblock %}
+--EXCEPTION--
+Twig_Error_Syntax: Cannot extend from a block in "index.twig" at line 3
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_isolation.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_isolation.test
new file mode 100644 (file)
index 0000000..6281671
--- /dev/null
@@ -0,0 +1,20 @@
+--TEST--
+"extends" tag
+--TEMPLATE--
+{% extends "base.twig" %}
+{% block content %}{% include "included.twig" %}{% endblock %}
+
+{% block footer %}Footer{% endblock %}
+--TEMPLATE(included.twig)--
+{% extends "base.twig" %}
+{% block content %}Included Content{% endblock %}
+--TEMPLATE(base.twig)--
+{% block content %}Default Content{% endblock %}
+
+{% block footer %}Default Footer{% endblock %}
+--DATA--
+return array()
+--EXPECT--
+Included Content
+Default Footer
+Footer
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_nested.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_nested.test
new file mode 100644 (file)
index 0000000..71e7c20
--- /dev/null
@@ -0,0 +1,28 @@
+--TEST--
+"extends" tag
+--TEMPLATE--
+{% extends "foo.twig" %}
+
+{% block content %}
+  {% block inside %}
+    INSIDE OVERRIDDEN
+  {% endblock %}
+
+  BEFORE
+  {{ parent() }}
+  AFTER
+{% endblock %}
+--TEMPLATE(foo.twig)--
+{% block content %}
+  BAR
+{% endblock %}
+--DATA--
+return array()
+--EXPECT--
+
+INSIDE OVERRIDDEN
+  
+  BEFORE
+    BAR
+
+  AFTER
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends.test
new file mode 100644 (file)
index 0000000..a9eaa4c
--- /dev/null
@@ -0,0 +1,8 @@
+--TEST--
+"parent" tag
+--TEMPLATE--
+{% block content %}
+    {{ parent() }}
+{% endblock %}
+--EXCEPTION--
+Twig_Error_Syntax: Calling "parent" on a template that does not extend nor "use" another template is forbidden in "index.twig" at line 3
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends_but_traits.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends_but_traits.test
new file mode 100644 (file)
index 0000000..63c7305
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+"parent" tag
+--TEMPLATE--
+{% use 'foo.twig' %}
+
+{% block content %}
+    {{ parent() }}
+{% endblock %}
+--TEMPLATE(foo.twig)--
+{% block content %}BAR{% endblock %}
+--DATA--
+return array()
+--EXPECT--
+BAR
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/template_instance.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/template_instance.test
new file mode 100644 (file)
index 0000000..d1876a5
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+"extends" tag accepts Twig_Template instance
+--TEMPLATE--
+{% extends foo %}
+
+{% block content %}
+{{ parent() }}FOO
+{% endblock %}
+--TEMPLATE(foo.twig)--
+{% block content %}BAR{% endblock %}
+--DATA--
+return array('foo' => $twig->loadTemplate('foo.twig'))
+--EXPECT--
+BARFOO
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/use.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/use.test
new file mode 100644 (file)
index 0000000..8f9ece7
--- /dev/null
@@ -0,0 +1,44 @@
+--TEST--
+"parent" function
+--TEMPLATE--
+{% extends "parent.twig" %}
+
+{% use "use1.twig" %}
+{% use "use2.twig" %}
+
+{% block content_parent %}
+    {{ parent() }}
+{% endblock %}
+
+{% block content_use1 %}
+    {{ parent() }}
+{% endblock %}
+
+{% block content_use2 %}
+    {{ parent() }}
+{% endblock %}
+
+{% block content %}
+    {{ block('content_use1_only') }}
+    {{ block('content_use2_only') }}
+{% endblock %}
+--TEMPLATE(parent.twig)--
+{% block content_parent 'content_parent' %}
+{% block content_use1 'content_parent' %}
+{% block content_use2 'content_parent' %}
+{% block content '' %}
+--TEMPLATE(use1.twig)--
+{% block content_use1 'content_use1' %}
+{% block content_use2 'content_use1' %}
+{% block content_use1_only 'content_use1_only' %}
+--TEMPLATE(use2.twig)--
+{% block content_use2 'content_use2' %}
+{% block content_use2_only 'content_use2_only' %}
+--DATA--
+return array()
+--EXPECT--
+    content_parent
+    content_use1
+    content_use2
+    content_use1_only
+    content_use2_only
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/basic.test
new file mode 100644 (file)
index 0000000..eef0c10
--- /dev/null
@@ -0,0 +1,17 @@
+--TEST--
+"macro" tag
+--TEMPLATE--
+{% import _self as macros %}
+
+{{ macros.input('username') }}
+{{ macros.input('password', null, 'password', 1) }}
+
+{% macro input(name, value, type, size) %}
+  <input type="{{ type|default("text") }}" name="{{ name }}" value="{{ value|e|default('') }}" size="{{ size|default(20) }}">
+{% endmacro %}
+--DATA--
+return array()
+--EXPECT--
+  <input type="text" name="username" value="" size="20">
+
+  <input type="password" name="password" value="" size="1">
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/endmacro_name.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/endmacro_name.test
new file mode 100644 (file)
index 0000000..ae6203b
--- /dev/null
@@ -0,0 +1,16 @@
+--TEST--
+"macro" tag supports name for endmacro
+--TEMPLATE--
+{% import _self as macros %}
+
+{{ macros.foo() }}
+{{ macros.bar() }}
+
+{% macro foo() %}foo{% endmacro %}
+{% macro bar() %}bar{% endmacro bar %}
+--DATA--
+return array()
+--EXPECT--
+foo
+bar
+
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/external.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/external.test
new file mode 100644 (file)
index 0000000..5cd3dae
--- /dev/null
@@ -0,0 +1,17 @@
+--TEST--
+"macro" tag
+--TEMPLATE--
+{% import 'forms.twig' as forms %}
+
+{{ forms.input('username') }}
+{{ forms.input('password', null, 'password', 1) }}
+--TEMPLATE(forms.twig)--
+{% macro input(name, value, type, size) %}
+  <input type="{{ type|default("text") }}" name="{{ name }}" value="{{ value|e|default('') }}" size="{{ size|default(20) }}">
+{% endmacro %}
+--DATA--
+return array()
+--EXPECT--
+  <input type="text" name="username" value="" size="20">
+
+  <input type="password" name="password" value="" size="1">
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from.test
new file mode 100644 (file)
index 0000000..205f591
--- /dev/null
@@ -0,0 +1,18 @@
+--TEST--
+"macro" tag
+--TEMPLATE--
+{% from 'forms.twig' import foo %}
+{% from 'forms.twig' import foo as foobar, bar %}
+
+{{ foo('foo') }}
+{{ foobar('foo') }}
+{{ bar('foo') }}
+--TEMPLATE(forms.twig)--
+{% macro foo(name) %}foo{{ name }}{% endmacro %}
+{% macro bar(name) %}bar{{ name }}{% endmacro %}
+--DATA--
+return array()
+--EXPECT--
+foofoo
+foofoo
+barfoo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/global.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/global.test
new file mode 100644 (file)
index 0000000..6b37176
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+"macro" tag
+--TEMPLATE--
+{% from 'forms.twig' import foo %}
+
+{{ foo('foo') }}
+{{ foo() }}
+--TEMPLATE(forms.twig)--
+{% macro foo(name) %}{{ name|default('foo') }}{{ global }}{% endmacro %}
+--DATA--
+return array()
+--EXPECT--
+fooglobal
+fooglobal
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/self_import.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/self_import.test
new file mode 100644 (file)
index 0000000..17756cb
--- /dev/null
@@ -0,0 +1,17 @@
+--TEST--
+"macro" tag
+--TEMPLATE--
+{% import _self as forms %}
+
+{{ forms.input('username') }}
+{{ forms.input('password', null, 'password', 1) }}
+
+{% macro input(name, value, type, size) %}
+  <input type="{{ type|default("text") }}" name="{{ name }}" value="{{ value|e|default('') }}" size="{{ size|default(20) }}">
+{% endmacro %}
+--DATA--
+return array()
+--EXPECT--
+  <input type="text" name="username" value="" size="20">
+
+  <input type="password" name="password" value="" size="1">
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/special_chars.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/special_chars.test
new file mode 100644 (file)
index 0000000..3721770
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+"§" as a macro name
+--TEMPLATE--
+{% import _self as macros %}
+
+{{ macros.§('foo') }}
+
+{% macro §(foo) %}
+  §{{ foo }}§
+{% endmacro %}
+--DATA--
+return array()
+--EXPECT--
+§foo§
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/basic.test
new file mode 100644 (file)
index 0000000..0445e85
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+"raw" tag
+--TEMPLATE--
+{% raw %}
+{{ foo }}
+{% endraw %}
+--DATA--
+return array()
+--EXPECT--
+{{ foo }}
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/mixed_usage_with_raw.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/mixed_usage_with_raw.test
new file mode 100644 (file)
index 0000000..2fd9fb2
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+"raw" tag
+--TEMPLATE--
+{% raw %}
+{{ foo }}
+{% endverbatim %}
+--DATA--
+return array()
+--EXCEPTION--
+Twig_Error_Syntax: Unexpected end of file: Unclosed "raw" block in "index.twig" at line 2
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/whitespace_control.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/whitespace_control.test
new file mode 100644 (file)
index 0000000..352bb18
--- /dev/null
@@ -0,0 +1,56 @@
+--TEST--
+"raw" tag
+--TEMPLATE--
+1***
+
+{%- raw %}
+    {{ 'bla' }}
+{% endraw %}
+
+1***
+2***
+
+{%- raw -%}
+    {{ 'bla' }}
+{% endraw %}
+
+2***
+3***
+
+{%- raw -%}
+    {{ 'bla' }}
+{% endraw -%}
+
+3***
+4***
+
+{%- raw -%}
+    {{ 'bla' }}
+{%- endraw %}
+
+4***
+5***
+
+{%- raw -%}
+    {{ 'bla' }}
+{%- endraw -%}
+
+5***
+--DATA--
+return array()
+--EXPECT--
+1***
+    {{ 'bla' }}
+
+
+1***
+2***{{ 'bla' }}
+
+
+2***
+3***{{ 'bla' }}
+3***
+4***{{ 'bla' }}
+
+4***
+5***{{ 'bla' }}5***
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/not_valid1.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/not_valid1.test
new file mode 100644 (file)
index 0000000..683c59a
--- /dev/null
@@ -0,0 +1,11 @@
+--TEST--
+sandbox tag
+--TEMPLATE--
+{%- sandbox %}
+    {%- include "foo.twig" %}
+    a
+{%- endsandbox %}
+--TEMPLATE(foo.twig)--
+foo
+--EXCEPTION--
+Twig_Error_Syntax: Only "include" tags are allowed within a "sandbox" section in "index.twig" at line 4
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/not_valid2.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/not_valid2.test
new file mode 100644 (file)
index 0000000..3dcfa88
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+sandbox tag
+--TEMPLATE--
+{%- sandbox %}
+    {%- include "foo.twig" %}
+
+    {% if 1 %}
+        {%- include "foo.twig" %}
+    {% endif %}
+{%- endsandbox %}
+--TEMPLATE(foo.twig)--
+foo
+--EXCEPTION--
+Twig_Error_Syntax: Only "include" tags are allowed within a "sandbox" section in "index.twig" at line 5
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/simple.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/simple.test
new file mode 100644 (file)
index 0000000..de20f3d
--- /dev/null
@@ -0,0 +1,22 @@
+--TEST--
+sandbox tag
+--TEMPLATE--
+{%- sandbox %}
+    {%- include "foo.twig" %}
+{%- endsandbox %}
+
+{%- sandbox %}
+    {%- include "foo.twig" %}
+    {%- include "foo.twig" %}
+{%- endsandbox %}
+
+{%- sandbox %}{% include "foo.twig" %}{% endsandbox %}
+--TEMPLATE(foo.twig)--
+foo
+--DATA--
+return array()
+--EXPECT--
+foo
+foo
+foo
+foo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/basic.test
new file mode 100644 (file)
index 0000000..a5a9f83
--- /dev/null
@@ -0,0 +1,20 @@
+--TEST--
+"set" tag
+--TEMPLATE--
+{% set foo = 'foo' %}
+{% set bar = 'foo<br />' %}
+
+{{ foo }}
+{{ bar }}
+
+{% set foo, bar = 'foo', 'bar' %}
+
+{{ foo }}{{ bar }}
+--DATA--
+return array()
+--EXPECT--
+foo
+foo&lt;br /&gt;
+
+
+foobar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/capture-empty.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/capture-empty.test
new file mode 100644 (file)
index 0000000..ec657f0
--- /dev/null
@@ -0,0 +1,9 @@
+--TEST--
+"set" tag block empty capture
+--TEMPLATE--
+{% set foo %}{% endset %}
+
+{% if foo %}FAIL{% endif %}
+--DATA--
+return array()
+--EXPECT--
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/capture.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/capture.test
new file mode 100644 (file)
index 0000000..f156a1a
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+"set" tag block capture
+--TEMPLATE--
+{% set foo %}f<br />o<br />o{% endset %}
+
+{{ foo }}
+--DATA--
+return array()
+--EXPECT--
+f<br />o<br />o
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/expression.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/expression.test
new file mode 100644 (file)
index 0000000..8ff434a
--- /dev/null
@@ -0,0 +1,12 @@
+--TEST--
+"set" tag
+--TEMPLATE--
+{% set foo, bar = 'foo' ~ 'bar', 'bar' ~ 'foo' %}
+
+{{ foo }}
+{{ bar }}
+--DATA--
+return array()
+--EXPECT--
+foobar
+barfoo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/spaceless/simple.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/spaceless/simple.test
new file mode 100644 (file)
index 0000000..dd06dec
--- /dev/null
@@ -0,0 +1,12 @@
+--TEST--
+"spaceless" tag removes whites between HTML tags
+--TEMPLATE--
+{% spaceless %}
+
+    <div>   <div>   foo   </div>   </div>
+
+{% endspaceless %}
+--DATA--
+return array()
+--EXPECT--
+<div><div>   foo   </div></div>
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/special_chars.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/special_chars.test
new file mode 100644 (file)
index 0000000..789b4ba
--- /dev/null
@@ -0,0 +1,8 @@
+--TEST--
+"§" custom tag
+--TEMPLATE--
+{% § %}
+--DATA--
+return array()
+--EXPECT--
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/trim_block.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/trim_block.test
new file mode 100644 (file)
index 0000000..1d2273f
--- /dev/null
@@ -0,0 +1,74 @@
+--TEST--
+Whitespace trimming on tags.
+--TEMPLATE--
+{{ 5 * '{#-'|length }}
+{{ '{{-'|length * 5 + '{%-'|length }}
+
+Trim on control tag:
+{% for i in range(1, 9) -%}
+       {{ i }}
+{%- endfor %}
+
+
+Trim on output tag:
+{% for i in range(1, 9) %}
+       {{- i -}}
+{% endfor %}
+
+
+Trim comments:
+      
+{#- Invisible -#}
+       
+After the comment.
+
+Trim leading space:
+{% if leading %}
+
+               {{- leading }}
+{% endif %}
+
+{%- if leading %}
+       {{- leading }}
+
+{%- endif %}
+
+
+Trim trailing space:
+{% if trailing -%}          
+       {{ trailing -}}
+
+{% endif -%}
+
+Combined:
+
+{%- if both -%}
+<ul>
+       <li>    {{- both -}}   </li>
+</ul>
+
+{%- endif -%}
+
+end
+--DATA--
+return array('leading' => 'leading space', 'trailing' => 'trailing space', 'both' => 'both')
+--EXPECT--
+15
+18
+
+Trim on control tag:
+123456789
+
+Trim on output tag:
+123456789
+
+Trim comments:After the comment.
+
+Trim leading space:
+leading space
+leading space
+
+Trim trailing space:
+trailing spaceCombined:<ul>
+       <li>both</li>
+</ul>end
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/aliases.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/aliases.test
new file mode 100644 (file)
index 0000000..f887006
--- /dev/null
@@ -0,0 +1,12 @@
+--TEST--
+"use" tag
+--TEMPLATE--
+{% use "blocks.twig" with content as foo %}
+
+{{ block('foo') }}
+--TEMPLATE(blocks.twig)--
+{% block content 'foo' %}
+--DATA--
+return array()
+--EXPECT--
+foo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/basic.test
new file mode 100644 (file)
index 0000000..7364d76
--- /dev/null
@@ -0,0 +1,12 @@
+--TEST--
+"use" tag
+--TEMPLATE--
+{% use "blocks.twig" %}
+
+{{ block('content') }}
+--TEMPLATE(blocks.twig)--
+{% block content 'foo' %}
+--DATA--
+return array()
+--EXPECT--
+foo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/deep.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/deep.test
new file mode 100644 (file)
index 0000000..b551a1e
--- /dev/null
@@ -0,0 +1,22 @@
+--TEST--
+"use" tag
+--TEMPLATE--
+{% use "foo.twig" %}
+
+{{ block('content') }}
+{{ block('foo') }}
+{{ block('bar') }}
+--TEMPLATE(foo.twig)--
+{% use "bar.twig" %}
+
+{% block content 'foo' %}
+{% block foo 'foo' %}
+--TEMPLATE(bar.twig)--
+{% block content 'bar' %}
+{% block bar 'bar' %}
+--DATA--
+return array()
+--EXPECT--
+foo
+foo
+bar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/deep_empty.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/deep_empty.test
new file mode 100644 (file)
index 0000000..05cca68
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+"use" tag
+--TEMPLATE--
+{% use "foo.twig" %}
+--TEMPLATE(foo.twig)--
+{% use "bar.twig" %}
+--TEMPLATE(bar.twig)--
+--DATA--
+return array()
+--EXPECT--
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/multiple.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/multiple.test
new file mode 100644 (file)
index 0000000..198be0c
--- /dev/null
@@ -0,0 +1,21 @@
+--TEST--
+"use" tag
+--TEMPLATE--
+{% use "foo.twig" %}
+{% use "bar.twig" %}
+
+{{ block('content') }}
+{{ block('foo') }}
+{{ block('bar') }}
+--TEMPLATE(foo.twig)--
+{% block content 'foo' %}
+{% block foo 'foo' %}
+--TEMPLATE(bar.twig)--
+{% block content 'bar' %}
+{% block bar 'bar' %}
+--DATA--
+return array()
+--EXPECT--
+bar
+foo
+bar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/multiple_aliases.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/multiple_aliases.test
new file mode 100644 (file)
index 0000000..8de871a
--- /dev/null
@@ -0,0 +1,23 @@
+--TEST--
+"use" tag
+--TEMPLATE--
+{% use "foo.twig" with content as foo_content %}
+{% use "bar.twig" %}
+
+{{ block('content') }}
+{{ block('foo') }}
+{{ block('bar') }}
+{{ block('foo_content') }}
+--TEMPLATE(foo.twig)--
+{% block content 'foo' %}
+{% block foo 'foo' %}
+--TEMPLATE(bar.twig)--
+{% block content 'bar' %}
+{% block bar 'bar' %}
+--DATA--
+return array()
+--EXPECT--
+bar
+foo
+bar
+foo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/basic.test
new file mode 100644 (file)
index 0000000..a95be55
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+"verbatim" tag
+--TEMPLATE--
+{% verbatim %}
+{{ foo }}
+{% endverbatim %}
+--DATA--
+return array()
+--EXPECT--
+{{ foo }}
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/mixed_usage_with_raw.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/mixed_usage_with_raw.test
new file mode 100644 (file)
index 0000000..941dddc
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+"verbatim" tag
+--TEMPLATE--
+{% verbatim %}
+{{ foo }}
+{% endraw %}
+--DATA--
+return array()
+--EXCEPTION--
+Twig_Error_Syntax: Unexpected end of file: Unclosed "verbatim" block in "index.twig" at line 2
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/whitespace_control.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/whitespace_control.test
new file mode 100644 (file)
index 0000000..eb61044
--- /dev/null
@@ -0,0 +1,56 @@
+--TEST--
+"verbatim" tag
+--TEMPLATE--
+1***
+
+{%- verbatim %}
+    {{ 'bla' }}
+{% endverbatim %}
+
+1***
+2***
+
+{%- verbatim -%}
+    {{ 'bla' }}
+{% endverbatim %}
+
+2***
+3***
+
+{%- verbatim -%}
+    {{ 'bla' }}
+{% endverbatim -%}
+
+3***
+4***
+
+{%- verbatim -%}
+    {{ 'bla' }}
+{%- endverbatim %}
+
+4***
+5***
+
+{%- verbatim -%}
+    {{ 'bla' }}
+{%- endverbatim -%}
+
+5***
+--DATA--
+return array()
+--EXPECT--
+1***
+    {{ 'bla' }}
+
+
+1***
+2***{{ 'bla' }}
+
+
+2***
+3***{{ 'bla' }}
+3***
+4***{{ 'bla' }}
+
+4***
+5***{{ 'bla' }}5***
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/array.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/array.test
new file mode 100644 (file)
index 0000000..1429d37
--- /dev/null
@@ -0,0 +1,24 @@
+--TEST--
+array index test
+--TEMPLATE--
+{% for key, value in days %}
+{{ key }}
+{% endfor %}
+--DATA--
+return array('days' => array(
+    1  => array('money' => 9),
+    2  => array('money' => 21),
+    3  => array('money' => 38),
+    4  => array('money' => 6),
+    18 => array('money' => 6),
+    19 => array('money' => 3),
+    31 => array('money' => 11),
+));
+--EXPECT--
+1
+2
+3
+4
+18
+19
+31
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/constant.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/constant.test
new file mode 100644 (file)
index 0000000..60218ac
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+"const" test
+--TEMPLATE--
+{{ 8 is constant('E_NOTICE') ? 'ok' : 'no' }}
+{{ 'bar' is constant('TwigTestFoo::BAR_NAME') ? 'ok' : 'no' }}
+{{ value is constant('TwigTestFoo::BAR_NAME') ? 'ok' : 'no' }}
+{{ 2 is constant('ARRAY_AS_PROPS', object) ? 'ok' : 'no' }}
+--DATA--
+return array('value' => 'bar', 'object' => new ArrayObject(array('hi')));
+--EXPECT--
+ok
+ok
+ok
+ok
\ No newline at end of file
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined.test
new file mode 100644 (file)
index 0000000..cbfe03d
--- /dev/null
@@ -0,0 +1,108 @@
+--TEST--
+"defined" test
+--TEMPLATE--
+{{ definedVar                     is     defined ? 'ok' : 'ko' }}
+{{ definedVar                     is not defined ? 'ko' : 'ok' }}
+{{ undefinedVar                   is     defined ? 'ko' : 'ok' }}
+{{ undefinedVar                   is not defined ? 'ok' : 'ko' }}
+{{ zeroVar                        is     defined ? 'ok' : 'ko' }}
+{{ nullVar                        is     defined ? 'ok' : 'ko' }}
+{{ nested.definedVar              is     defined ? 'ok' : 'ko' }}
+{{ nested['definedVar']           is     defined ? 'ok' : 'ko' }}
+{{ nested.definedVar              is not defined ? 'ko' : 'ok' }}
+{{ nested.undefinedVar            is     defined ? 'ko' : 'ok' }}
+{{ nested['undefinedVar']         is     defined ? 'ko' : 'ok' }}
+{{ nested.undefinedVar            is not defined ? 'ok' : 'ko' }}
+{{ nested.zeroVar                 is     defined ? 'ok' : 'ko' }}
+{{ nested.nullVar                 is     defined ? 'ok' : 'ko' }}
+{{ nested.definedArray.0          is     defined ? 'ok' : 'ko' }}
+{{ nested['definedArray'][0]      is     defined ? 'ok' : 'ko' }}
+{{ object.foo                     is     defined ? 'ok' : 'ko' }}
+{{ object.undefinedMethod         is     defined ? 'ko' : 'ok' }}
+{{ object.getFoo()                is     defined ? 'ok' : 'ko' }}
+{{ object.getFoo('a')             is     defined ? 'ok' : 'ko' }}
+{{ object.undefinedMethod()       is     defined ? 'ko' : 'ok' }}
+{{ object.undefinedMethod('a')    is     defined ? 'ko' : 'ok' }}
+{{ object.self.foo                is     defined ? 'ok' : 'ko' }}
+{{ object.self.undefinedMethod    is     defined ? 'ko' : 'ok' }}
+{{ object.undefinedMethod.self    is     defined ? 'ko' : 'ok' }}
+--DATA--
+return array(
+    'definedVar' => 'defined',
+    'zeroVar'    => 0,
+    'nullVar'    => null,
+    'nested'      => array(
+        'definedVar'   => 'defined',
+        'zeroVar'      => 0,
+        'nullVar'      => null,
+        'definedArray' => array(0),
+    ),
+    'object' => new TwigTestFoo(),
+);
+--EXPECT--
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+--DATA--
+return array(
+    'definedVar' => 'defined',
+    'zeroVar'    => 0,
+    'nullVar'    => null,
+    'nested'      => array(
+        'definedVar'   => 'defined',
+        'zeroVar'      => 0,
+        'nullVar'      => null,
+        'definedArray' => array(0),
+    ),
+    'object' => new TwigTestFoo(),
+);
+--CONFIG--
+return array('strict_variables' => false)
+--EXPECT--
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/empty.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/empty.test
new file mode 100644 (file)
index 0000000..a776d03
--- /dev/null
@@ -0,0 +1,45 @@
+--TEST--
+"empty" test
+--TEMPLATE--
+{{ foo is empty ? 'ok' : 'ko' }}
+{{ bar is empty ? 'ok' : 'ko' }}
+{{ foobar is empty ? 'ok' : 'ko' }}
+{{ array is empty ? 'ok' : 'ko' }}
+{{ zero is empty ? 'ok' : 'ko' }}
+{{ string is empty ? 'ok' : 'ko' }}
+{{ countable_empty is empty ? 'ok' : 'ko' }}
+{{ countable_not_empty is empty ? 'ok' : 'ko' }}
+{{ markup_empty is empty ? 'ok' : 'ko' }}
+{{ markup_not_empty is empty ? 'ok' : 'ko' }}
+--DATA--
+
+class CountableStub implements Countable
+{
+    private $items;
+
+    public function __construct(array $items)
+    {
+        $this->items = $items;
+    }
+
+    public function count()
+    {
+        return count($this->items);
+    }
+}
+return array(
+    'foo' => '', 'bar' => null, 'foobar' => false, 'array' => array(), 'zero' => 0, 'string' => '0',
+    'countable_empty' => new CountableStub(array()), 'countable_not_empty' => new CountableStub(array(1, 2)),
+    'markup_empty' => new Twig_Markup('', 'UTF-8'), 'markup_not_empty' => new Twig_Markup('test', 'UTF-8'),
+);
+--EXPECT--
+ok
+ok
+ok
+ok
+ko
+ko
+ok
+ko
+ok
+ko
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/even.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/even.test
new file mode 100644 (file)
index 0000000..695b4c2
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+"even" test
+--TEMPLATE--
+{{ 1 is even ? 'ko' : 'ok' }}
+{{ 2 is even ? 'ok' : 'ko' }}
+{{ 1 is not even ? 'ok' : 'ko' }}
+{{ 2 is not even ? 'ko' : 'ok' }}
+--DATA--
+return array()
+--EXPECT--
+ok
+ok
+ok
+ok
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/in.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/in.test
new file mode 100644 (file)
index 0000000..45c72fd
--- /dev/null
@@ -0,0 +1,48 @@
+--TEST--
+Twig supports the in operator
+--TEMPLATE--
+{% if bar in foo %}
+TRUE
+{% endif %}
+{% if not (bar in foo) %}
+{% else %}
+TRUE
+{% endif %}
+{% if bar not in foo %}
+{% else %}
+TRUE
+{% endif %}
+{% if 'a' in bar %}
+TRUE
+{% endif %}
+{% if 'c' not in bar %}
+TRUE
+{% endif %}
+{% if '' not in bar %}
+TRUE
+{% endif %}
+{% if '' in '' %}
+TRUE
+{% endif %}
+{% if '0' not in '' %}
+TRUE
+{% endif %}
+{% if 'a' not in '0' %}
+TRUE
+{% endif %}
+{% if '0' in '0' %}
+TRUE
+{% endif %}
+--DATA--
+return array('bar' => 'bar', 'foo' => array('bar' => 'bar'))
+--EXPECT--
+TRUE
+TRUE
+TRUE
+TRUE
+TRUE
+TRUE
+TRUE
+TRUE
+TRUE
+TRUE
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/in_with_objects.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/in_with_objects.test
new file mode 100644 (file)
index 0000000..8e08061
--- /dev/null
@@ -0,0 +1,19 @@
+--TEST--
+Twig supports the in operator when using objects
+--TEMPLATE--
+{% if object in object_list %}
+TRUE
+{% endif %}
+--DATA--
+$foo = new TwigTestFoo();
+$foo1 = new TwigTestFoo();
+
+$foo->position = $foo1;
+$foo1->position = $foo;
+
+return array(
+    'object'      => $foo,
+    'object_list' => array($foo1, $foo),
+);
+--EXPECT--
+TRUE
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/iterable.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/iterable.test
new file mode 100644 (file)
index 0000000..ec52550
--- /dev/null
@@ -0,0 +1,19 @@
+--TEST--
+"iterable" test
+--TEMPLATE--
+{{ foo is iterable ? 'ok' : 'ko' }}
+{{ traversable is iterable ? 'ok' : 'ko' }}
+{{ obj is iterable ? 'ok' : 'ko' }}
+{{ val is iterable ? 'ok' : 'ko' }}
+--DATA--
+return array(
+    'foo' => array(),
+    'traversable' => new ArrayIterator(array()),
+    'obj' => new stdClass(),
+    'val' => 'test',
+);
+--EXPECT--
+ok
+ok
+ko
+ko
\ No newline at end of file
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/odd.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/odd.test
new file mode 100644 (file)
index 0000000..1b8311e
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+"odd" test
+--TEMPLATE--
+{{ 1 is odd ? 'ok' : 'ko' }}
+{{ 2 is odd ? 'ko' : 'ok' }}
+--DATA--
+return array()
+--EXPECT--
+ok
+ok
\ No newline at end of file
diff --git a/vendor/twig/twig/test/Twig/Tests/IntegrationTest.php b/vendor/twig/twig/test/Twig/Tests/IntegrationTest.php
new file mode 100644 (file)
index 0000000..5feb8f4
--- /dev/null
@@ -0,0 +1,217 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+// This function is defined to check that escaping strategies
+// like html works even if a function with the same name is defined.
+function html()
+{
+    return 'foo';
+}
+
+class Twig_Tests_IntegrationTest extends Twig_Test_IntegrationTestCase
+{
+    public function getExtensions()
+    {
+        $policy = new Twig_Sandbox_SecurityPolicy(array(), array(), array(), array(), array());
+
+        return array(
+            new Twig_Extension_Debug(),
+            new Twig_Extension_Sandbox($policy, false),
+            new Twig_Extension_StringLoader(),
+            new TwigTestExtension(),
+        );
+    }
+
+    public function getFixturesDir()
+    {
+        return dirname(__FILE__).'/Fixtures/';
+    }
+}
+
+function test_foo($value = 'foo')
+{
+    return $value;
+}
+
+class TwigTestFoo implements Iterator
+{
+    const BAR_NAME = 'bar';
+
+    public $position = 0;
+    public $array = array(1, 2);
+
+    public function bar($param1 = null, $param2 = null)
+    {
+        return 'bar'.($param1 ? '_'.$param1 : '').($param2 ? '-'.$param2 : '');
+    }
+
+    public function getFoo()
+    {
+        return 'foo';
+    }
+
+    public function getSelf()
+    {
+        return $this;
+    }
+
+    public function is()
+    {
+        return 'is';
+    }
+
+    public function in()
+    {
+        return 'in';
+    }
+
+    public function not()
+    {
+        return 'not';
+    }
+
+    public function strToLower($value)
+    {
+        return strtolower($value);
+    }
+
+    public function rewind()
+    {
+        $this->position = 0;
+    }
+
+    public function current()
+    {
+        return $this->array[$this->position];
+    }
+
+    public function key()
+    {
+        return 'a';
+    }
+
+    public function next()
+    {
+        ++$this->position;
+    }
+
+    public function valid()
+    {
+        return isset($this->array[$this->position]);
+    }
+}
+
+class TwigTestTokenParser_§ extends Twig_TokenParser
+{
+    public function parse(Twig_Token $token)
+    {
+        $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
+
+        return new Twig_Node_Print(new Twig_Node_Expression_Constant('§', -1), -1);
+    }
+
+    public function getTag()
+    {
+        return '§';
+    }
+}
+
+class TwigTestExtension extends Twig_Extension
+{
+    public function getTokenParsers()
+    {
+        return array(
+            new TwigTestTokenParser_§(),
+        );
+    }
+
+    public function getFilters()
+    {
+        return array(
+            '§'                => new Twig_Filter_Method($this, '§Filter'),
+            'escape_and_nl2br' => new Twig_Filter_Method($this, 'escape_and_nl2br', array('needs_environment' => true, 'is_safe' => array('html'))),
+            'nl2br'            => new Twig_Filter_Method($this, 'nl2br', array('pre_escape' => 'html', 'is_safe' => array('html'))),
+            'escape_something' => new Twig_Filter_Method($this, 'escape_something', array('is_safe' => array('something'))),
+            'preserves_safety' => new Twig_Filter_Method($this, 'preserves_safety', array('preserves_safety' => array('html'))),
+            '*_path'           => new Twig_Filter_Method($this, 'dynamic_path'),
+            '*_foo_*_bar'      => new Twig_Filter_Method($this, 'dynamic_foo'),
+        );
+    }
+
+    public function getFunctions()
+    {
+        return array(
+            '§'           => new Twig_Function_Method($this, '§Function'),
+            'safe_br'     => new Twig_Function_Method($this, 'br', array('is_safe' => array('html'))),
+            'unsafe_br'   => new Twig_Function_Method($this, 'br'),
+            '*_path'      => new Twig_Function_Method($this, 'dynamic_path'),
+            '*_foo_*_bar' => new Twig_Function_Method($this, 'dynamic_foo'),
+        );
+    }
+
+    public function §Filter($value)
+    {
+        return "§{$value}§";
+    }
+
+    public function §Function($value)
+    {
+        return "§{$value}§";
+    }
+
+    /**
+     * nl2br which also escapes, for testing escaper filters
+     */
+    public function escape_and_nl2br($env, $value, $sep = '<br />')
+    {
+        return $this->nl2br(twig_escape_filter($env, $value, 'html'), $sep);
+    }
+
+    /**
+     * nl2br only, for testing filters with pre_escape
+     */
+    public function nl2br($value, $sep = '<br />')
+    {
+        // not secure if $value contains html tags (not only entities)
+        // don't use
+        return str_replace("\n", "$sep\n", $value);
+    }
+
+    public function dynamic_path($element, $item)
+    {
+        return $element.'/'.$item;
+    }
+
+    public function dynamic_foo($foo, $bar, $item)
+    {
+        return $foo.'/'.$bar.'/'.$item;
+    }
+
+    public function escape_something($value)
+    {
+        return strtoupper($value);
+    }
+
+    public function preserves_safety($value)
+    {
+        return strtoupper($value);
+    }
+
+    public function br()
+    {
+        return '<br />';
+    }
+
+    public function getName()
+    {
+        return 'integration_test';
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/LexerTest.php b/vendor/twig/twig/test/Twig/Tests/LexerTest.php
new file mode 100644 (file)
index 0000000..9f3c751
--- /dev/null
@@ -0,0 +1,301 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase
+{
+    public function testNameLabelForTag()
+    {
+        $template = '{% § %}';
+
+        $lexer = new Twig_Lexer(new Twig_Environment());
+        $stream = $lexer->tokenize($template);
+
+        $stream->expect(Twig_Token::BLOCK_START_TYPE);
+        $this->assertSame('§', $stream->expect(Twig_Token::NAME_TYPE)->getValue());
+    }
+
+    public function testNameLabelForFunction()
+    {
+        $template = '{{ §() }}';
+
+        $lexer = new Twig_Lexer(new Twig_Environment());
+        $stream = $lexer->tokenize($template);
+
+        $stream->expect(Twig_Token::VAR_START_TYPE);
+        $this->assertSame('§', $stream->expect(Twig_Token::NAME_TYPE)->getValue());
+    }
+
+    public function testBracketsNesting()
+    {
+        $template = '{{ {"a":{"b":"c"}} }}';
+
+        $this->assertEquals(2, $this->countToken($template, Twig_Token::PUNCTUATION_TYPE, '{'));
+        $this->assertEquals(2, $this->countToken($template, Twig_Token::PUNCTUATION_TYPE, '}'));
+    }
+
+    protected function countToken($template, $type, $value = null)
+    {
+        $lexer = new Twig_Lexer(new Twig_Environment());
+        $stream = $lexer->tokenize($template);
+
+        $count = 0;
+        $tokens = array();
+        while (!$stream->isEOF()) {
+            $token = $stream->next();
+            if ($type === $token->getType()) {
+                if (null === $value || $value === $token->getValue()) {
+                    ++$count;
+                }
+            }
+        }
+
+        return $count;
+    }
+
+    public function testLineDirective()
+    {
+        $template = "foo\n"
+            . "bar\n"
+            . "{% line 10 %}\n"
+            . "{{\n"
+            . "baz\n"
+            . "}}\n";
+
+        $lexer = new Twig_Lexer(new Twig_Environment());
+        $stream = $lexer->tokenize($template);
+
+        // foo\nbar\n
+        $this->assertSame(1, $stream->expect(Twig_Token::TEXT_TYPE)->getLine());
+        // \n (after {% line %})
+        $this->assertSame(10, $stream->expect(Twig_Token::TEXT_TYPE)->getLine());
+        // {{
+        $this->assertSame(11, $stream->expect(Twig_Token::VAR_START_TYPE)->getLine());
+        // baz
+        $this->assertSame(12, $stream->expect(Twig_Token::NAME_TYPE)->getLine());
+    }
+
+    public function testLineDirectiveInline()
+    {
+        $template = "foo\n"
+            . "bar{% line 10 %}{{\n"
+            . "baz\n"
+            . "}}\n";
+
+        $lexer = new Twig_Lexer(new Twig_Environment());
+        $stream = $lexer->tokenize($template);
+
+        // foo\nbar
+        $this->assertSame(1, $stream->expect(Twig_Token::TEXT_TYPE)->getLine());
+        // {{
+        $this->assertSame(10, $stream->expect(Twig_Token::VAR_START_TYPE)->getLine());
+        // baz
+        $this->assertSame(11, $stream->expect(Twig_Token::NAME_TYPE)->getLine());
+    }
+
+    public function testLongComments()
+    {
+        $template = '{# '.str_repeat('*', 100000).' #}';
+
+        $lexer = new Twig_Lexer(new Twig_Environment());
+        $lexer->tokenize($template);
+
+        // should not throw an exception
+    }
+
+    public function testLongRaw()
+    {
+        $template = '{% raw %}'.str_repeat('*', 100000).'{% endraw %}';
+
+        $lexer = new Twig_Lexer(new Twig_Environment());
+        $stream = $lexer->tokenize($template);
+
+        // should not throw an exception
+    }
+
+    public function testLongVar()
+    {
+        $template = '{{ '.str_repeat('x', 100000).' }}';
+
+        $lexer = new Twig_Lexer(new Twig_Environment());
+        $stream = $lexer->tokenize($template);
+
+        // should not throw an exception
+    }
+
+    public function testLongBlock()
+    {
+        $template = '{% '.str_repeat('x', 100000).' %}';
+
+        $lexer = new Twig_Lexer(new Twig_Environment());
+        $stream = $lexer->tokenize($template);
+
+        // should not throw an exception
+    }
+
+    public function testBigNumbers()
+    {
+        $template = '{{ 922337203685477580700 }}';
+
+        $lexer = new Twig_Lexer(new Twig_Environment());
+        $stream = $lexer->tokenize($template);
+        $node = $stream->next();
+        $node = $stream->next();
+        $this->assertEquals(922337203685477580700, $node->getValue());
+    }
+
+    public function testStringWithEscapedDelimiter()
+    {
+        $tests = array(
+            "{{ 'foo \' bar' }}" => 'foo \' bar',
+            '{{ "foo \" bar" }}' => "foo \" bar",
+        );
+        $lexer = new Twig_Lexer(new Twig_Environment());
+        foreach ($tests as $template => $expected) {
+            $stream = $lexer->tokenize($template);
+            $stream->expect(Twig_Token::VAR_START_TYPE);
+            $stream->expect(Twig_Token::STRING_TYPE, $expected);
+        }
+    }
+
+    public function testStringWithInterpolation()
+    {
+        $template = 'foo {{ "bar #{ baz + 1 }" }}';
+
+        $lexer = new Twig_Lexer(new Twig_Environment());
+        $stream = $lexer->tokenize($template);
+        $stream->expect(Twig_Token::TEXT_TYPE, 'foo ');
+        $stream->expect(Twig_Token::VAR_START_TYPE);
+        $stream->expect(Twig_Token::STRING_TYPE, 'bar ');
+        $stream->expect(Twig_Token::INTERPOLATION_START_TYPE);
+        $stream->expect(Twig_Token::NAME_TYPE, 'baz');
+        $stream->expect(Twig_Token::OPERATOR_TYPE, '+');
+        $stream->expect(Twig_Token::NUMBER_TYPE, '1');
+        $stream->expect(Twig_Token::INTERPOLATION_END_TYPE);
+        $stream->expect(Twig_Token::VAR_END_TYPE);
+    }
+
+    public function testStringWithEscapedInterpolation()
+    {
+        $template = '{{ "bar \#{baz+1}" }}';
+
+        $lexer = new Twig_Lexer(new Twig_Environment());
+        $stream = $lexer->tokenize($template);
+        $stream->expect(Twig_Token::VAR_START_TYPE);
+        $stream->expect(Twig_Token::STRING_TYPE, 'bar #{baz+1}');
+        $stream->expect(Twig_Token::VAR_END_TYPE);
+    }
+
+    public function testStringWithHash()
+    {
+        $template = '{{ "bar # baz" }}';
+
+        $lexer = new Twig_Lexer(new Twig_Environment());
+        $stream = $lexer->tokenize($template);
+        $stream->expect(Twig_Token::VAR_START_TYPE);
+        $stream->expect(Twig_Token::STRING_TYPE, 'bar # baz');
+        $stream->expect(Twig_Token::VAR_END_TYPE);
+    }
+
+    /**
+     * @expectedException Twig_Error_Syntax
+     * @expectedExceptionMessage Unclosed """
+     */
+    public function testStringWithUnterminatedInterpolation()
+    {
+        $template = '{{ "bar #{x" }}';
+
+        $lexer = new Twig_Lexer(new Twig_Environment());
+        $stream = $lexer->tokenize($template);
+    }
+
+    public function testStringWithNestedInterpolations()
+    {
+        $template = '{{ "bar #{ "foo#{bar}" }" }}';
+
+        $lexer = new Twig_Lexer(new Twig_Environment());
+        $stream = $lexer->tokenize($template);
+        $stream->expect(Twig_Token::VAR_START_TYPE);
+        $stream->expect(Twig_Token::STRING_TYPE, 'bar ');
+        $stream->expect(Twig_Token::INTERPOLATION_START_TYPE);
+        $stream->expect(Twig_Token::STRING_TYPE, 'foo');
+        $stream->expect(Twig_Token::INTERPOLATION_START_TYPE);
+        $stream->expect(Twig_Token::NAME_TYPE, 'bar');
+        $stream->expect(Twig_Token::INTERPOLATION_END_TYPE);
+        $stream->expect(Twig_Token::INTERPOLATION_END_TYPE);
+        $stream->expect(Twig_Token::VAR_END_TYPE);
+    }
+
+    public function testStringWithNestedInterpolationsInBlock()
+    {
+        $template = '{% foo "bar #{ "foo#{bar}" }" %}';
+
+        $lexer = new Twig_Lexer(new Twig_Environment());
+        $stream = $lexer->tokenize($template);
+        $stream->expect(Twig_Token::BLOCK_START_TYPE);
+        $stream->expect(Twig_Token::NAME_TYPE, 'foo');
+        $stream->expect(Twig_Token::STRING_TYPE, 'bar ');
+        $stream->expect(Twig_Token::INTERPOLATION_START_TYPE);
+        $stream->expect(Twig_Token::STRING_TYPE, 'foo');
+        $stream->expect(Twig_Token::INTERPOLATION_START_TYPE);
+        $stream->expect(Twig_Token::NAME_TYPE, 'bar');
+        $stream->expect(Twig_Token::INTERPOLATION_END_TYPE);
+        $stream->expect(Twig_Token::INTERPOLATION_END_TYPE);
+        $stream->expect(Twig_Token::BLOCK_END_TYPE);
+    }
+
+    public function testOperatorEndingWithALetterAtTheEndOfALine()
+    {
+        $template = "{{ 1 and\n0}}";
+
+        $lexer = new Twig_Lexer(new Twig_Environment());
+        $stream = $lexer->tokenize($template);
+        $stream->expect(Twig_Token::VAR_START_TYPE);
+        $stream->expect(Twig_Token::NUMBER_TYPE, 1);
+        $stream->expect(Twig_Token::OPERATOR_TYPE, 'and');
+    }
+
+    /**
+     * @expectedException Twig_Error_Syntax
+     * @expectedExceptionMessage Unclosed "variable" at line 3
+     */
+    public function testUnterminatedVariable()
+    {
+        $template = '
+
+{{
+
+bar
+
+
+';
+
+        $lexer = new Twig_Lexer(new Twig_Environment());
+        $stream = $lexer->tokenize($template);
+    }
+
+    /**
+     * @expectedException Twig_Error_Syntax
+     * @expectedExceptionMessage Unclosed "block" at line 3
+     */
+    public function testUnterminatedBlock()
+    {
+        $template = '
+
+{%
+
+bar
+
+
+';
+
+        $lexer = new Twig_Lexer(new Twig_Environment());
+        $stream = $lexer->tokenize($template);
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Loader/ArrayTest.php b/vendor/twig/twig/test/Twig/Tests/Loader/ArrayTest.php
new file mode 100644 (file)
index 0000000..1369a6b
--- /dev/null
@@ -0,0 +1,97 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Loader_ArrayTest extends PHPUnit_Framework_TestCase
+{
+    public function testGetSource()
+    {
+        $loader = new Twig_Loader_Array(array('foo' => 'bar'));
+
+        $this->assertEquals('bar', $loader->getSource('foo'));
+    }
+
+    /**
+     * @expectedException Twig_Error_Loader
+     */
+    public function testGetSourceWhenTemplateDoesNotExist()
+    {
+        $loader = new Twig_Loader_Array(array());
+
+        $loader->getSource('foo');
+    }
+
+    public function testGetCacheKey()
+    {
+        $loader = new Twig_Loader_Array(array('foo' => 'bar'));
+
+        $this->assertEquals('bar', $loader->getCacheKey('foo'));
+    }
+
+    /**
+     * @expectedException Twig_Error_Loader
+     */
+    public function testGetCacheKeyWhenTemplateDoesNotExist()
+    {
+        $loader = new Twig_Loader_Array(array());
+
+        $loader->getCacheKey('foo');
+    }
+
+    public function testSetTemplate()
+    {
+        $loader = new Twig_Loader_Array(array());
+        $loader->setTemplate('foo', 'bar');
+
+        $this->assertEquals('bar', $loader->getSource('foo'));
+    }
+
+    public function testIsFresh()
+    {
+        $loader = new Twig_Loader_Array(array('foo' => 'bar'));
+        $this->assertTrue($loader->isFresh('foo', time()));
+    }
+
+    /**
+     * @expectedException Twig_Error_Loader
+     */
+    public function testIsFreshWhenTemplateDoesNotExist()
+    {
+        $loader = new Twig_Loader_Array(array());
+
+        $loader->isFresh('foo', time());
+    }
+
+    public function testTemplateReference()
+    {
+        $name = new Twig_Test_Loader_TemplateReference('foo');
+        $loader = new Twig_Loader_Array(array('foo' => 'bar'));
+
+        $loader->getCacheKey($name);
+        $loader->getSource($name);
+        $loader->isFresh($name, time());
+        $loader->setTemplate($name, 'foobar');
+    }
+}
+
+class Twig_Test_Loader_TemplateReference
+{
+    private $name;
+
+    public function __construct($name)
+    {
+        $this->name = $name;
+    }
+
+    public function __toString()
+    {
+        return $this->name;
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Loader/ChainTest.php b/vendor/twig/twig/test/Twig/Tests/Loader/ChainTest.php
new file mode 100644 (file)
index 0000000..4fe0db9
--- /dev/null
@@ -0,0 +1,79 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Loader_ChainTest extends PHPUnit_Framework_TestCase
+{
+    public function testGetSource()
+    {
+        $loader = new Twig_Loader_Chain(array(
+            new Twig_Loader_Array(array('foo' => 'bar')),
+            new Twig_Loader_Array(array('foo' => 'foobar', 'bar' => 'foo')),
+        ));
+
+        $this->assertEquals('bar', $loader->getSource('foo'));
+        $this->assertEquals('foo', $loader->getSource('bar'));
+    }
+
+    /**
+     * @expectedException Twig_Error_Loader
+     */
+    public function testGetSourceWhenTemplateDoesNotExist()
+    {
+        $loader = new Twig_Loader_Chain(array());
+
+        $loader->getSource('foo');
+    }
+
+    public function testGetCacheKey()
+    {
+        $loader = new Twig_Loader_Chain(array(
+            new Twig_Loader_Array(array('foo' => 'bar')),
+            new Twig_Loader_Array(array('foo' => 'foobar', 'bar' => 'foo')),
+        ));
+
+        $this->assertEquals('bar', $loader->getCacheKey('foo'));
+        $this->assertEquals('foo', $loader->getCacheKey('bar'));
+    }
+
+    /**
+     * @expectedException Twig_Error_Loader
+     */
+    public function testGetCacheKeyWhenTemplateDoesNotExist()
+    {
+        $loader = new Twig_Loader_Chain(array());
+
+        $loader->getCacheKey('foo');
+    }
+
+    public function testAddLoader()
+    {
+        $loader = new Twig_Loader_Chain();
+        $loader->addLoader(new Twig_Loader_Array(array('foo' => 'bar')));
+
+        $this->assertEquals('bar', $loader->getSource('foo'));
+    }
+
+    public function testExists()
+    {
+        $loader1 = $this->getMock('Twig_Loader_Array', array('exists', 'getSource'), array(), '', false);
+        $loader1->expects($this->once())->method('exists')->will($this->returnValue(false));
+        $loader1->expects($this->never())->method('getSource');
+
+        $loader2 = $this->getMock('Twig_LoaderInterface');
+        $loader2->expects($this->once())->method('getSource')->will($this->returnValue('content'));
+
+        $loader = new Twig_Loader_Chain();
+        $loader->addLoader($loader1);
+        $loader->addLoader($loader2);
+
+        $this->assertTrue($loader->exists('foo'));
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Loader/FilesystemTest.php b/vendor/twig/twig/test/Twig/Tests/Loader/FilesystemTest.php
new file mode 100644 (file)
index 0000000..4c874b6
--- /dev/null
@@ -0,0 +1,97 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Loader_FilesystemTest extends PHPUnit_Framework_TestCase
+{
+    /**
+     * @dataProvider getSecurityTests
+     */
+    public function testSecurity($template)
+    {
+        $loader = new Twig_Loader_Filesystem(array(dirname(__FILE__).'/../Fixtures'));
+
+        try {
+            $loader->getCacheKey($template);
+            $this->fail();
+        } catch (Twig_Error_Loader $e) {
+            $this->assertNotContains('Unable to find template', $e->getMessage());
+        }
+    }
+
+    public function getSecurityTests()
+    {
+        return array(
+            array("AutoloaderTest\0.php"),
+            array('..\\AutoloaderTest.php'),
+            array('..\\\\\\AutoloaderTest.php'),
+            array('../AutoloaderTest.php'),
+            array('..////AutoloaderTest.php'),
+            array('./../AutoloaderTest.php'),
+            array('.\\..\\AutoloaderTest.php'),
+            array('././././././../AutoloaderTest.php'),
+            array('.\\./.\\./.\\./../AutoloaderTest.php'),
+            array('foo/../../AutoloaderTest.php'),
+            array('foo\\..\\..\\AutoloaderTest.php'),
+            array('foo/../bar/../../AutoloaderTest.php'),
+            array('foo/bar/../../../AutoloaderTest.php'),
+            array('filters/../../AutoloaderTest.php'),
+            array('filters//..//..//AutoloaderTest.php'),
+            array('filters\\..\\..\\AutoloaderTest.php'),
+            array('filters\\\\..\\\\..\\\\AutoloaderTest.php'),
+            array('filters\\//../\\/\\..\\AutoloaderTest.php'),
+            array('/../AutoloaderTest.php'),
+        );
+    }
+
+    public function testPaths()
+    {
+        $basePath = dirname(__FILE__).'/Fixtures';
+
+        $loader = new Twig_Loader_Filesystem(array($basePath.'/normal', $basePath.'/normal_bis'));
+        $loader->setPaths(array($basePath.'/named', $basePath.'/named_bis'), 'named');
+        $loader->addPath($basePath.'/named_ter', 'named');
+        $loader->addPath($basePath.'/normal_ter');
+        $loader->prependPath($basePath.'/normal_final');
+        $loader->prependPath($basePath.'/named_final', 'named');
+
+        $this->assertEquals(array(
+            $basePath.'/normal_final',
+            $basePath.'/normal',
+            $basePath.'/normal_bis',
+            $basePath.'/normal_ter',
+        ), $loader->getPaths());
+        $this->assertEquals(array(
+            $basePath.'/named_final',
+            $basePath.'/named',
+            $basePath.'/named_bis',
+            $basePath.'/named_ter',
+        ), $loader->getPaths('named'));
+
+        $this->assertEquals("path (final)\n", $loader->getSource('index.html'));
+        $this->assertEquals("path (final)\n", $loader->getSource('@__main__/index.html'));
+        $this->assertEquals("named path (final)\n", $loader->getSource('@named/index.html'));
+    }
+
+    public function testEmptyConstructor()
+    {
+        $loader = new Twig_Loader_Filesystem();
+        $this->assertEquals(array(), $loader->getPaths());
+    }
+
+    public function testGetNamespaces()
+    {
+        $loader = new Twig_Loader_Filesystem(sys_get_temp_dir());
+        $this->assertEquals(array(Twig_Loader_Filesystem::MAIN_NAMESPACE), $loader->getNamespaces());
+
+        $loader->addPath(sys_get_temp_dir(), 'named');
+        $this->assertEquals(array(Twig_Loader_Filesystem::MAIN_NAMESPACE, 'named'), $loader->getNamespaces());
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named/index.html b/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named/index.html
new file mode 100644 (file)
index 0000000..9e5449c
--- /dev/null
@@ -0,0 +1 @@
+named path
diff --git a/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named_bis/index.html b/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named_bis/index.html
new file mode 100644 (file)
index 0000000..d3a272b
--- /dev/null
@@ -0,0 +1 @@
+named path (bis)
diff --git a/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named_final/index.html b/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named_final/index.html
new file mode 100644 (file)
index 0000000..9f05d15
--- /dev/null
@@ -0,0 +1 @@
+named path (final)
diff --git a/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named_ter/index.html b/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named_ter/index.html
new file mode 100644 (file)
index 0000000..24fb68a
--- /dev/null
@@ -0,0 +1 @@
+named path (ter)
diff --git a/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal/index.html b/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal/index.html
new file mode 100644 (file)
index 0000000..e7a8fd4
--- /dev/null
@@ -0,0 +1 @@
+path
diff --git a/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal_bis/index.html b/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal_bis/index.html
new file mode 100644 (file)
index 0000000..bfa9160
--- /dev/null
@@ -0,0 +1 @@
+path (bis)
diff --git a/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal_final/index.html b/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal_final/index.html
new file mode 100644 (file)
index 0000000..73a089b
--- /dev/null
@@ -0,0 +1 @@
+path (final)
diff --git a/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal_ter/index.html b/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal_ter/index.html
new file mode 100644 (file)
index 0000000..b7ad97d
--- /dev/null
@@ -0,0 +1 @@
+path (ter)
diff --git a/vendor/twig/twig/test/Twig/Tests/NativeExtensionTest.php b/vendor/twig/twig/test/Twig/Tests/NativeExtensionTest.php
new file mode 100644 (file)
index 0000000..3fafd33
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_NativeExtensionTest extends PHPUnit_Framework_TestCase
+{
+    public function testGetProperties()
+    {
+        $twig = new Twig_Environment(new Twig_Loader_String(), array(
+            'debug'      => true,
+            'cache'      => false,
+            'autoescape' => false
+        ));
+
+        $d1 = new DateTime();
+        $d2 = new DateTime();
+        $output = $twig->render('{{ d1.date }}{{ d2.date }}', compact('d1', 'd2'));
+
+        // If it fails, PHP will crash.
+        $this->assertEquals($output, $d1->date . $d2->date);
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/AutoEscapeTest.php b/vendor/twig/twig/test/Twig/Tests/Node/AutoEscapeTest.php
new file mode 100644 (file)
index 0000000..608446b
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_AutoEscapeTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_AutoEscape::__construct
+     */
+    public function testConstructor()
+    {
+        $body = new Twig_Node(array(new Twig_Node_Text('foo', 1)));
+        $node = new Twig_Node_AutoEscape(true, $body, 1);
+
+        $this->assertEquals($body, $node->getNode('body'));
+        $this->assertEquals(true, $node->getAttribute('value'));
+    }
+
+    /**
+     * @covers Twig_Node_AutoEscape::compile
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $body = new Twig_Node(array(new Twig_Node_Text('foo', 1)));
+        $node = new Twig_Node_AutoEscape(true, $body, 1);
+
+        return array(
+            array($node, "// line 1\necho \"foo\";"),
+        );
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/BlockReferenceTest.php b/vendor/twig/twig/test/Twig/Tests/Node/BlockReferenceTest.php
new file mode 100644 (file)
index 0000000..96d0e10
--- /dev/null
@@ -0,0 +1,43 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_BlockReferenceTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_BlockReference::__construct
+     */
+    public function testConstructor()
+    {
+        $node = new Twig_Node_BlockReference('foo', 1);
+
+        $this->assertEquals('foo', $node->getAttribute('name'));
+    }
+
+    /**
+     * @covers Twig_Node_BlockReference::compile
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        return array(
+            array(new Twig_Node_BlockReference('foo', 1), <<<EOF
+// line 1
+\$this->displayBlock('foo', \$context, \$blocks);
+EOF
+            ),
+        );
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/BlockTest.php b/vendor/twig/twig/test/Twig/Tests/Node/BlockTest.php
new file mode 100644 (file)
index 0000000..024049d
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_BlockTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Block::__construct
+     */
+    public function testConstructor()
+    {
+        $body = new Twig_Node_Text('foo', 1);
+        $node = new Twig_Node_Block('foo', $body, 1);
+
+        $this->assertEquals($body, $node->getNode('body'));
+        $this->assertEquals('foo', $node->getAttribute('name'));
+    }
+
+    /**
+     * @covers Twig_Node_Block::compile
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $body = new Twig_Node_Text('foo', 1);
+        $node = new Twig_Node_Block('foo', $body, 1);
+
+        return array(
+            array($node, <<<EOF
+// line 1
+public function block_foo(\$context, array \$blocks = array())
+{
+    echo "foo";
+}
+EOF
+            ),
+        );
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/DoTest.php b/vendor/twig/twig/test/Twig/Tests/Node/DoTest.php
new file mode 100644 (file)
index 0000000..a406e22
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_DoTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Do::__construct
+     */
+    public function testConstructor()
+    {
+        $expr = new Twig_Node_Expression_Constant('foo', 1);
+        $node = new Twig_Node_Do($expr, 1);
+
+        $this->assertEquals($expr, $node->getNode('expr'));
+    }
+
+    /**
+     * @covers Twig_Node_Do::compile
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $tests = array();
+
+        $expr = new Twig_Node_Expression_Constant('foo', 1);
+        $node = new Twig_Node_Do($expr, 1);
+        $tests[] = array($node, "// line 1\n\"foo\";");
+
+        return $tests;
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/ArrayTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/ArrayTest.php
new file mode 100644 (file)
index 0000000..c6a9044
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_Expression_ArrayTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Expression_Array::__construct
+     */
+    public function testConstructor()
+    {
+        $elements = array(new Twig_Node_Expression_Constant('foo', 1), $foo = new Twig_Node_Expression_Constant('bar', 1));
+        $node = new Twig_Node_Expression_Array($elements, 1);
+
+        $this->assertEquals($foo, $node->getNode(1));
+    }
+
+    /**
+     * @covers Twig_Node_Expression_Array::compile
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $elements = array(
+            new Twig_Node_Expression_Constant('foo', 1),
+            new Twig_Node_Expression_Constant('bar', 1),
+
+            new Twig_Node_Expression_Constant('bar', 1),
+            new Twig_Node_Expression_Constant('foo', 1),
+        );
+        $node = new Twig_Node_Expression_Array($elements, 1);
+
+        return array(
+            array($node, 'array("foo" => "bar", "bar" => "foo")'),
+        );
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/AssignNameTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/AssignNameTest.php
new file mode 100644 (file)
index 0000000..b156dcc
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_Expression_AssignNameTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Expression_AssignName::__construct
+     */
+    public function testConstructor()
+    {
+        $node = new Twig_Node_Expression_AssignName('foo', 1);
+
+        $this->assertEquals('foo', $node->getAttribute('name'));
+    }
+
+    /**
+     * @covers Twig_Node_Expression_AssignName::compile
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $node = new Twig_Node_Expression_AssignName('foo', 1);
+
+        return array(
+            array($node, '$context["foo"]'),
+        );
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/AddTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/AddTest.php
new file mode 100644 (file)
index 0000000..a0f49cb
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_Expression_Binary_AddTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Expression_Binary_Add::__construct
+     */
+    public function testConstructor()
+    {
+        $left = new Twig_Node_Expression_Constant(1, 1);
+        $right = new Twig_Node_Expression_Constant(2, 1);
+        $node = new Twig_Node_Expression_Binary_Add($left, $right, 1);
+
+        $this->assertEquals($left, $node->getNode('left'));
+        $this->assertEquals($right, $node->getNode('right'));
+    }
+
+    /**
+     * @covers Twig_Node_Expression_Binary_Add::compile
+     * @covers Twig_Node_Expression_Binary_Add::operator
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $left = new Twig_Node_Expression_Constant(1, 1);
+        $right = new Twig_Node_Expression_Constant(2, 1);
+        $node = new Twig_Node_Expression_Binary_Add($left, $right, 1);
+
+        return array(
+            array($node, '(1 + 2)'),
+        );
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/AndTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/AndTest.php
new file mode 100644 (file)
index 0000000..50e551a
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_Expression_Binary_AndTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Expression_Binary_And::__construct
+     */
+    public function testConstructor()
+    {
+        $left = new Twig_Node_Expression_Constant(1, 1);
+        $right = new Twig_Node_Expression_Constant(2, 1);
+        $node = new Twig_Node_Expression_Binary_And($left, $right, 1);
+
+        $this->assertEquals($left, $node->getNode('left'));
+        $this->assertEquals($right, $node->getNode('right'));
+    }
+
+    /**
+     * @covers Twig_Node_Expression_Binary_And::compile
+     * @covers Twig_Node_Expression_Binary_And::operator
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $left = new Twig_Node_Expression_Constant(1, 1);
+        $right = new Twig_Node_Expression_Constant(2, 1);
+        $node = new Twig_Node_Expression_Binary_And($left, $right, 1);
+
+        return array(
+            array($node, '(1 && 2)'),
+        );
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/ConcatTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/ConcatTest.php
new file mode 100644 (file)
index 0000000..140329f
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_Expression_Binary_ConcatTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Expression_Binary_Concat::__construct
+     */
+    public function testConstructor()
+    {
+        $left = new Twig_Node_Expression_Constant(1, 1);
+        $right = new Twig_Node_Expression_Constant(2, 1);
+        $node = new Twig_Node_Expression_Binary_Concat($left, $right, 1);
+
+        $this->assertEquals($left, $node->getNode('left'));
+        $this->assertEquals($right, $node->getNode('right'));
+    }
+
+    /**
+     * @covers Twig_Node_Expression_Binary_Concat::compile
+     * @covers Twig_Node_Expression_Binary_Concat::operator
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $left = new Twig_Node_Expression_Constant(1, 1);
+        $right = new Twig_Node_Expression_Constant(2, 1);
+        $node = new Twig_Node_Expression_Binary_Concat($left, $right, 1);
+
+        return array(
+            array($node, '(1 . 2)'),
+        );
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/DivTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/DivTest.php
new file mode 100644 (file)
index 0000000..0c1a3c7
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_Expression_Binary_DivTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Expression_Binary_Div::__construct
+     */
+    public function testConstructor()
+    {
+        $left = new Twig_Node_Expression_Constant(1, 1);
+        $right = new Twig_Node_Expression_Constant(2, 1);
+        $node = new Twig_Node_Expression_Binary_Div($left, $right, 1);
+
+        $this->assertEquals($left, $node->getNode('left'));
+        $this->assertEquals($right, $node->getNode('right'));
+    }
+
+    /**
+     * @covers Twig_Node_Expression_Binary_Div::compile
+     * @covers Twig_Node_Expression_Binary_Div::operator
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $left = new Twig_Node_Expression_Constant(1, 1);
+        $right = new Twig_Node_Expression_Constant(2, 1);
+        $node = new Twig_Node_Expression_Binary_Div($left, $right, 1);
+
+        return array(
+            array($node, '(1 / 2)'),
+        );
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/FloorDivTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/FloorDivTest.php
new file mode 100644 (file)
index 0000000..ead1fde
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_Expression_Binary_FloorDivTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Expression_Binary_FloorDiv::__construct
+     */
+    public function testConstructor()
+    {
+        $left = new Twig_Node_Expression_Constant(1, 1);
+        $right = new Twig_Node_Expression_Constant(2, 1);
+        $node = new Twig_Node_Expression_Binary_FloorDiv($left, $right, 1);
+
+        $this->assertEquals($left, $node->getNode('left'));
+        $this->assertEquals($right, $node->getNode('right'));
+    }
+
+    /**
+     * @covers Twig_Node_Expression_Binary_FloorDiv::compile
+     * @covers Twig_Node_Expression_Binary_FloorDiv::operator
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $left = new Twig_Node_Expression_Constant(1, 1);
+        $right = new Twig_Node_Expression_Constant(2, 1);
+        $node = new Twig_Node_Expression_Binary_FloorDiv($left, $right, 1);
+
+        return array(
+            array($node, 'intval(floor((1 / 2)))'),
+        );
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/ModTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/ModTest.php
new file mode 100644 (file)
index 0000000..4fe1a1f
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_Expression_Binary_ModTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Expression_Binary_Mod::__construct
+     */
+    public function testConstructor()
+    {
+        $left = new Twig_Node_Expression_Constant(1, 1);
+        $right = new Twig_Node_Expression_Constant(2, 1);
+        $node = new Twig_Node_Expression_Binary_Mod($left, $right, 1);
+
+        $this->assertEquals($left, $node->getNode('left'));
+        $this->assertEquals($right, $node->getNode('right'));
+    }
+
+    /**
+     * @covers Twig_Node_Expression_Binary_Mod::compile
+     * @covers Twig_Node_Expression_Binary_Mod::operator
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $left = new Twig_Node_Expression_Constant(1, 1);
+        $right = new Twig_Node_Expression_Constant(2, 1);
+        $node = new Twig_Node_Expression_Binary_Mod($left, $right, 1);
+
+        return array(
+            array($node, '(1 % 2)'),
+        );
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/MulTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/MulTest.php
new file mode 100644 (file)
index 0000000..12bb35c
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_Expression_Binary_MulTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Expression_Binary_Mul::__construct
+     */
+    public function testConstructor()
+    {
+        $left = new Twig_Node_Expression_Constant(1, 1);
+        $right = new Twig_Node_Expression_Constant(2, 1);
+        $node = new Twig_Node_Expression_Binary_Mul($left, $right, 1);
+
+        $this->assertEquals($left, $node->getNode('left'));
+        $this->assertEquals($right, $node->getNode('right'));
+    }
+
+    /**
+     * @covers Twig_Node_Expression_Binary_Mul::compile
+     * @covers Twig_Node_Expression_Binary_Mul::operator
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $left = new Twig_Node_Expression_Constant(1, 1);
+        $right = new Twig_Node_Expression_Constant(2, 1);
+        $node = new Twig_Node_Expression_Binary_Mul($left, $right, 1);
+
+        return array(
+            array($node, '(1 * 2)'),
+        );
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/OrTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/OrTest.php
new file mode 100644 (file)
index 0000000..9534c41
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_Expression_Binary_OrTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Expression_Binary_Or::__construct
+     */
+    public function testConstructor()
+    {
+        $left = new Twig_Node_Expression_Constant(1, 1);
+        $right = new Twig_Node_Expression_Constant(2, 1);
+        $node = new Twig_Node_Expression_Binary_Or($left, $right, 1);
+
+        $this->assertEquals($left, $node->getNode('left'));
+        $this->assertEquals($right, $node->getNode('right'));
+    }
+
+    /**
+     * @covers Twig_Node_Expression_Binary_Or::compile
+     * @covers Twig_Node_Expression_Binary_Or::operator
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $left = new Twig_Node_Expression_Constant(1, 1);
+        $right = new Twig_Node_Expression_Constant(2, 1);
+        $node = new Twig_Node_Expression_Binary_Or($left, $right, 1);
+
+        return array(
+            array($node, '(1 || 2)'),
+        );
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/SubTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/SubTest.php
new file mode 100644 (file)
index 0000000..9074893
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_Expression_Binary_SubTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Expression_Binary_Sub::__construct
+     */
+    public function testConstructor()
+    {
+        $left = new Twig_Node_Expression_Constant(1, 1);
+        $right = new Twig_Node_Expression_Constant(2, 1);
+        $node = new Twig_Node_Expression_Binary_Sub($left, $right, 1);
+
+        $this->assertEquals($left, $node->getNode('left'));
+        $this->assertEquals($right, $node->getNode('right'));
+    }
+
+    /**
+     * @covers Twig_Node_Expression_Binary_Sub::compile
+     * @covers Twig_Node_Expression_Binary_Sub::operator
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $left = new Twig_Node_Expression_Constant(1, 1);
+        $right = new Twig_Node_Expression_Constant(2, 1);
+        $node = new Twig_Node_Expression_Binary_Sub($left, $right, 1);
+
+        return array(
+            array($node, '(1 - 2)'),
+        );
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/CallTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/CallTest.php
new file mode 100644 (file)
index 0000000..53b5e6e
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_Expression_CallTest extends PHPUnit_Framework_TestCase
+{
+    public function testGetArguments()
+    {
+        $node = new Twig_Tests_Node_Expression_Call(array(), array('type' => 'function', 'name' => 'date'));
+        $this->assertEquals(array('U'), $node->getArguments('date', array('format' => 'U')));
+    }
+
+    /**
+     * @expectedException        Twig_Error_Syntax
+     * @expectedExceptionMessage Positional arguments cannot be used after named arguments for function "date".
+     */
+    public function testGetArgumentsWhenPositionalArgumentsAfterNamedArguments()
+    {
+        $node = new Twig_Tests_Node_Expression_Call(array(), array('type' => 'function', 'name' => 'date'));
+        $node->getArguments('date', array('timestamp' => 123456, 'Y-m-d'));
+    }
+
+    /**
+     * @expectedException        Twig_Error_Syntax
+     * @expectedExceptionMessage Argument "format" is defined twice for function "date".
+     */
+    public function testGetArgumentsWhenArgumentIsDefinedTwice()
+    {
+        $node = new Twig_Tests_Node_Expression_Call(array(), array('type' => 'function', 'name' => 'date'));
+        $node->getArguments('date', array('Y-m-d', 'format' => 'U'));
+    }
+
+    /**
+     * @expectedException        Twig_Error_Syntax
+     * @expectedExceptionMessage Unknown argument "unknown" for function "date".
+     */
+    public function testGetArgumentsWithWrongNamedArgumentName()
+    {
+        $node = new Twig_Tests_Node_Expression_Call(array(), array('type' => 'function', 'name' => 'date'));
+        $node->getArguments('date', array('Y-m-d', 'unknown' => ''));
+    }
+
+    /**
+     * @expectedException        Twig_Error_Syntax
+     * @expectedExceptionMessage Unknown arguments "unknown1", "unknown2" for function "date".
+     */
+    public function testGetArgumentsWithWrongNamedArgumentNames()
+    {
+        $node = new Twig_Tests_Node_Expression_Call(array(), array('type' => 'function', 'name' => 'date'));
+        $node->getArguments('date', array('Y-m-d', 'unknown1' => '', 'unknown2' => ''));
+    }
+}
+
+class Twig_Tests_Node_Expression_Call extends Twig_Node_Expression_Call
+{
+    public function getArguments($callable, $arguments)
+    {
+        return parent::getArguments($callable, $arguments);
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/ConditionalTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/ConditionalTest.php
new file mode 100644 (file)
index 0000000..9906d51
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_Expression_ConditionalTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Expression_Conditional::__construct
+     */
+    public function testConstructor()
+    {
+        $expr1 = new Twig_Node_Expression_Constant(1, 1);
+        $expr2 = new Twig_Node_Expression_Constant(2, 1);
+        $expr3 = new Twig_Node_Expression_Constant(3, 1);
+        $node = new Twig_Node_Expression_Conditional($expr1, $expr2, $expr3, 1);
+
+        $this->assertEquals($expr1, $node->getNode('expr1'));
+        $this->assertEquals($expr2, $node->getNode('expr2'));
+        $this->assertEquals($expr3, $node->getNode('expr3'));
+    }
+
+    /**
+     * @covers Twig_Node_Expression_Conditional::compile
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $tests = array();
+
+        $expr1 = new Twig_Node_Expression_Constant(1, 1);
+        $expr2 = new Twig_Node_Expression_Constant(2, 1);
+        $expr3 = new Twig_Node_Expression_Constant(3, 1);
+        $node = new Twig_Node_Expression_Conditional($expr1, $expr2, $expr3, 1);
+        $tests[] = array($node, '((1) ? (2) : (3))');
+
+        return $tests;
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/ConstantTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/ConstantTest.php
new file mode 100644 (file)
index 0000000..d0dec53
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_Expression_ConstantTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Expression_Constant::__construct
+     */
+    public function testConstructor()
+    {
+        $node = new Twig_Node_Expression_Constant('foo', 1);
+
+        $this->assertEquals('foo', $node->getAttribute('value'));
+    }
+
+    /**
+     * @covers Twig_Node_Expression_Constant::compile
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $tests = array();
+
+        $node = new Twig_Node_Expression_Constant('foo', 1);
+        $tests[] = array($node, '"foo"');
+
+        return $tests;
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/FilterTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/FilterTest.php
new file mode 100644 (file)
index 0000000..8089b9c
--- /dev/null
@@ -0,0 +1,133 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_Expression_FilterTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Expression_Filter::__construct
+     */
+    public function testConstructor()
+    {
+        $expr = new Twig_Node_Expression_Constant('foo', 1);
+        $name = new Twig_Node_Expression_Constant('upper', 1);
+        $args = new Twig_Node();
+        $node = new Twig_Node_Expression_Filter($expr, $name, $args, 1);
+
+        $this->assertEquals($expr, $node->getNode('node'));
+        $this->assertEquals($name, $node->getNode('filter'));
+        $this->assertEquals($args, $node->getNode('arguments'));
+    }
+
+    /**
+     * @covers Twig_Node_Expression_Filter::compile
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $tests = array();
+
+        $expr = new Twig_Node_Expression_Constant('foo', 1);
+        $node = $this->createFilter($expr, 'upper');
+        $node = $this->createFilter($node, 'number_format', array(new Twig_Node_Expression_Constant(2, 1), new Twig_Node_Expression_Constant('.', 1), new Twig_Node_Expression_Constant(',', 1)));
+
+        if (function_exists('mb_get_info')) {
+            $tests[] = array($node, 'twig_number_format_filter($this->env, twig_upper_filter($this->env, "foo"), 2, ".", ",")');
+        } else {
+            $tests[] = array($node, 'twig_number_format_filter($this->env, strtoupper("foo"), 2, ".", ",")');
+        }
+
+        // named arguments
+        $date = new Twig_Node_Expression_Constant(0, 1);
+        $node = $this->createFilter($date, 'date', array(
+            'timezone' => new Twig_Node_Expression_Constant('America/Chicago', 1),
+            'format'   => new Twig_Node_Expression_Constant('d/m/Y H:i:s P', 1),
+        ));
+        $tests[] = array($node, 'twig_date_format_filter($this->env, 0, "d/m/Y H:i:s P", "America/Chicago")');
+
+        // skip an optional argument
+        $date = new Twig_Node_Expression_Constant(0, 1);
+        $node = $this->createFilter($date, 'date', array(
+            'timezone' => new Twig_Node_Expression_Constant('America/Chicago', 1),
+        ));
+        $tests[] = array($node, 'twig_date_format_filter($this->env, 0, null, "America/Chicago")');
+
+        // underscores vs camelCase for named arguments
+        $string = new Twig_Node_Expression_Constant('abc', 1);
+        $node = $this->createFilter($string, 'reverse', array(
+            'preserve_keys' => new Twig_Node_Expression_Constant(true, 1),
+        ));
+        $tests[] = array($node, 'twig_reverse_filter($this->env, "abc", true)');
+        $node = $this->createFilter($string, 'reverse', array(
+            'preserveKeys' => new Twig_Node_Expression_Constant(true, 1),
+        ));
+        $tests[] = array($node, 'twig_reverse_filter($this->env, "abc", true)');
+
+        // filter as an anonymous function
+        if (version_compare(phpversion(), '5.3.0', '>=')) {
+            $node = $this->createFilter(new Twig_Node_Expression_Constant('foo', 1), 'anonymous');
+            $tests[] = array($node, 'call_user_func_array($this->env->getFilter(\'anonymous\')->getCallable(), array("foo"))');
+        }
+
+        return $tests;
+    }
+
+    /**
+     * @expectedException        Twig_Error_Syntax
+     * @expectedExceptionMessage Unknown argument "foobar" for filter "date".
+     */
+    public function testCompileWithWrongNamedArgumentName()
+    {
+        $date = new Twig_Node_Expression_Constant(0, 1);
+        $node = $this->createFilter($date, 'date', array(
+            'foobar' => new Twig_Node_Expression_Constant('America/Chicago', 1),
+        ));
+
+        $compiler = $this->getCompiler();
+        $compiler->compile($node);
+    }
+
+    /**
+     * @expectedException        Twig_Error_Syntax
+     * @expectedExceptionMessage Value for argument "from" is required for filter "replace".
+     */
+    public function testCompileWithMissingNamedArgument()
+    {
+        $value = new Twig_Node_Expression_Constant(0, 1);
+        $node = $this->createFilter($value, 'replace', array(
+            'to' => new Twig_Node_Expression_Constant('foo', 1),
+        ));
+
+        $compiler = $this->getCompiler();
+        $compiler->compile($node);
+    }
+
+    protected function createFilter($node, $name, array $arguments = array())
+    {
+        $name = new Twig_Node_Expression_Constant($name, 1);
+        $arguments = new Twig_Node($arguments);
+
+        return new Twig_Node_Expression_Filter($node, $name, $arguments, 1);
+    }
+
+    protected function getEnvironment()
+    {
+        if (version_compare(phpversion(), '5.3.0', '>=')) {
+            return include 'PHP53/FilterInclude.php';
+        }
+
+        return parent::getEnvironment();
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/FunctionTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/FunctionTest.php
new file mode 100644 (file)
index 0000000..431dc38
--- /dev/null
@@ -0,0 +1,99 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_Expression_FunctionTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Expression_Function::__construct
+     */
+    public function testConstructor()
+    {
+        $name = 'function';
+        $args = new Twig_Node();
+        $node = new Twig_Node_Expression_Function($name, $args, 1);
+
+        $this->assertEquals($name, $node->getAttribute('name'));
+        $this->assertEquals($args, $node->getNode('arguments'));
+    }
+
+    /**
+     * @covers Twig_Node_Expression_Function::compile
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $environment = new Twig_Environment();
+        $environment->addFunction('foo', new Twig_Function_Function('foo', array()));
+        $environment->addFunction('bar', new Twig_Function_Function('bar', array('needs_environment' => true)));
+        $environment->addFunction('foofoo', new Twig_Function_Function('foofoo', array('needs_context' => true)));
+        $environment->addFunction('foobar', new Twig_Function_Function('foobar', array('needs_environment' => true, 'needs_context' => true)));
+
+        $tests = array();
+
+        $node = $this->createFunction('foo');
+        $tests[] = array($node, 'foo()', $environment);
+
+        $node = $this->createFunction('foo', array(new Twig_Node_Expression_Constant('bar', 1), new Twig_Node_Expression_Constant('foobar', 1)));
+        $tests[] = array($node, 'foo("bar", "foobar")', $environment);
+
+        $node = $this->createFunction('bar');
+        $tests[] = array($node, 'bar($this->env)', $environment);
+
+        $node = $this->createFunction('bar', array(new Twig_Node_Expression_Constant('bar', 1)));
+        $tests[] = array($node, 'bar($this->env, "bar")', $environment);
+
+        $node = $this->createFunction('foofoo');
+        $tests[] = array($node, 'foofoo($context)', $environment);
+
+        $node = $this->createFunction('foofoo', array(new Twig_Node_Expression_Constant('bar', 1)));
+        $tests[] = array($node, 'foofoo($context, "bar")', $environment);
+
+        $node = $this->createFunction('foobar');
+        $tests[] = array($node, 'foobar($this->env, $context)', $environment);
+
+        $node = $this->createFunction('foobar', array(new Twig_Node_Expression_Constant('bar', 1)));
+        $tests[] = array($node, 'foobar($this->env, $context, "bar")', $environment);
+
+        // named arguments
+        $node = $this->createFunction('date', array(
+            'timezone' => new Twig_Node_Expression_Constant('America/Chicago', 1),
+            'date'     => new Twig_Node_Expression_Constant(0, 1),
+        ));
+        $tests[] = array($node, 'twig_date_converter($this->env, 0, "America/Chicago")');
+
+        // function as an anonymous function
+        if (version_compare(phpversion(), '5.3.0', '>=')) {
+            $node = $this->createFunction('anonymous', array(new Twig_Node_Expression_Constant('foo', 1)));
+            $tests[] = array($node, 'call_user_func_array($this->env->getFunction(\'anonymous\')->getCallable(), array("foo"))');
+        }
+
+        return $tests;
+    }
+
+    protected function createFunction($name, array $arguments = array())
+    {
+        return new Twig_Node_Expression_Function($name, new Twig_Node($arguments), 1);
+    }
+
+    protected function getEnvironment()
+    {
+        if (version_compare(phpversion(), '5.3.0', '>=')) {
+            return include 'PHP53/FunctionInclude.php';
+        }
+
+        return parent::getEnvironment();
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/GetAttrTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/GetAttrTest.php
new file mode 100644 (file)
index 0000000..6a63cce
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_Expression_GetAttrTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Expression_GetAttr::__construct
+     */
+    public function testConstructor()
+    {
+        $expr = new Twig_Node_Expression_Name('foo', 1);
+        $attr = new Twig_Node_Expression_Constant('bar', 1);
+        $args = new Twig_Node_Expression_Array(array(), 1);
+        $args->addElement(new Twig_Node_Expression_Name('foo', 1));
+        $args->addElement(new Twig_Node_Expression_Constant('bar', 1));
+        $node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, Twig_TemplateInterface::ARRAY_CALL, 1);
+
+        $this->assertEquals($expr, $node->getNode('node'));
+        $this->assertEquals($attr, $node->getNode('attribute'));
+        $this->assertEquals($args, $node->getNode('arguments'));
+        $this->assertEquals(Twig_TemplateInterface::ARRAY_CALL, $node->getAttribute('type'));
+    }
+
+    /**
+     * @covers Twig_Node_Expression_GetAttr::compile
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $tests = array();
+
+        $expr = new Twig_Node_Expression_Name('foo', 1);
+        $attr = new Twig_Node_Expression_Constant('bar', 1);
+        $args = new Twig_Node_Expression_Array(array(), 1);
+        $node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, Twig_TemplateInterface::ANY_CALL, 1);
+        $tests[] = array($node, sprintf('%s%s, "bar")', $this->getAttributeGetter(), $this->getVariableGetter('foo')));
+
+        $node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, Twig_TemplateInterface::ARRAY_CALL, 1);
+        $tests[] = array($node, sprintf('%s%s, "bar", array(), "array")', $this->getAttributeGetter(), $this->getVariableGetter('foo')));
+
+        $args = new Twig_Node_Expression_Array(array(), 1);
+        $args->addElement(new Twig_Node_Expression_Name('foo', 1));
+        $args->addElement(new Twig_Node_Expression_Constant('bar', 1));
+        $node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, Twig_TemplateInterface::METHOD_CALL, 1);
+        $tests[] = array($node, sprintf('%s%s, "bar", array(0 => %s, 1 => "bar"), "method")', $this->getAttributeGetter(), $this->getVariableGetter('foo'), $this->getVariableGetter('foo')));
+
+        return $tests;
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/NameTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/NameTest.php
new file mode 100644 (file)
index 0000000..76d109b
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_Expression_NameTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Expression_Name::__construct
+     */
+    public function testConstructor()
+    {
+        $node = new Twig_Node_Expression_Name('foo', 1);
+
+        $this->assertEquals('foo', $node->getAttribute('name'));
+    }
+
+    /**
+     * @covers Twig_Node_Expression_Name::compile
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $node = new Twig_Node_Expression_Name('foo', 1);
+        $self = new Twig_Node_Expression_Name('_self', 1);
+        $context = new Twig_Node_Expression_Name('_context', 1);
+
+        $env = new Twig_Environment(null, array('strict_variables' => true));
+        $env1 = new Twig_Environment(null, array('strict_variables' => false));
+
+        return array(
+            version_compare(PHP_VERSION, '5.4.0') >= 0 ? array($node, '(isset($context["foo"]) ? $context["foo"] : $this->getContext($context, "foo"))', $env) : array($node, '$this->getContext($context, "foo")', $env),
+            array($node, $this->getVariableGetter('foo'), $env1),
+            array($self, '$this'),
+            array($context, '$context'),
+        );
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/PHP53/FilterInclude.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/PHP53/FilterInclude.php
new file mode 100644 (file)
index 0000000..15e3aa9
--- /dev/null
@@ -0,0 +1,6 @@
+<?php
+
+$env = new Twig_Environment();
+$env->addFilter(new Twig_SimpleFilter('anonymous', function () {}));
+
+return $env;
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/PHP53/FunctionInclude.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/PHP53/FunctionInclude.php
new file mode 100644 (file)
index 0000000..d2170ed
--- /dev/null
@@ -0,0 +1,6 @@
+<?php
+
+$env = new Twig_Environment();
+$env->addFunction(new Twig_SimpleFunction('anonymous', function () {}));
+
+return $env;
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/PHP53/TestInclude.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/PHP53/TestInclude.php
new file mode 100644 (file)
index 0000000..6366286
--- /dev/null
@@ -0,0 +1,6 @@
+<?php
+
+$env = new Twig_Environment();
+$env->addTest(new Twig_SimpleTest('anonymous', function () {}));
+
+return $env;
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/ParentTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/ParentTest.php
new file mode 100644 (file)
index 0000000..4d40419
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_Expression_ParentTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Expression_Parent::__construct
+     */
+    public function testConstructor()
+    {
+        $node = new Twig_Node_Expression_Parent('foo', 1);
+
+        $this->assertEquals('foo', $node->getAttribute('name'));
+    }
+
+    /**
+     * @covers Twig_Node_Expression_Parent::compile
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $tests = array();
+        $tests[] = array(new Twig_Node_Expression_Parent('foo', 1), '$this->renderParentBlock("foo", $context, $blocks)');
+
+        return $tests;
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/TestTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/TestTest.php
new file mode 100644 (file)
index 0000000..0664150
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_Expression_TestTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Expression_Test::__construct
+     */
+    public function testConstructor()
+    {
+        $expr = new Twig_Node_Expression_Constant('foo', 1);
+        $name = new Twig_Node_Expression_Constant('null', 1);
+        $args = new Twig_Node();
+        $node = new Twig_Node_Expression_Test($expr, $name, $args, 1);
+
+        $this->assertEquals($expr, $node->getNode('node'));
+        $this->assertEquals($args, $node->getNode('arguments'));
+        $this->assertEquals($name, $node->getAttribute('name'));
+    }
+
+    /**
+     * @covers Twig_Node_Expression_Test::compile
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $tests = array();
+
+        $expr = new Twig_Node_Expression_Constant('foo', 1);
+        $node = new Twig_Node_Expression_Test_Null($expr, 'null', new Twig_Node(array()), 1);
+        $tests[] = array($node, '(null === "foo")');
+
+        // test as an anonymous function
+        if (version_compare(phpversion(), '5.3.0', '>=')) {
+            $node = $this->createTest(new Twig_Node_Expression_Constant('foo', 1), 'anonymous', array(new Twig_Node_Expression_Constant('foo', 1)));
+            $tests[] = array($node, 'call_user_func_array($this->env->getTest(\'anonymous\')->getCallable(), array("foo", "foo"))');
+        }
+
+        return $tests;
+    }
+
+    protected function createTest($node, $name, array $arguments = array())
+    {
+        return new Twig_Node_Expression_Test($node, $name, new Twig_Node($arguments), 1);
+    }
+
+    protected function getEnvironment()
+    {
+        if (version_compare(phpversion(), '5.3.0', '>=')) {
+            return include 'PHP53/TestInclude.php';
+        }
+
+        return parent::getEnvironment();
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/Unary/NegTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Unary/NegTest.php
new file mode 100644 (file)
index 0000000..d55ab33
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_Expression_Unary_NegTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Expression_Unary_Neg::__construct
+     */
+    public function testConstructor()
+    {
+        $expr = new Twig_Node_Expression_Constant(1, 1);
+        $node = new Twig_Node_Expression_Unary_Neg($expr, 1);
+
+        $this->assertEquals($expr, $node->getNode('node'));
+    }
+
+    /**
+     * @covers Twig_Node_Expression_Unary_Neg::compile
+     * @covers Twig_Node_Expression_Unary_Neg::operator
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $node = new Twig_Node_Expression_Constant(1, 1);
+        $node = new Twig_Node_Expression_Unary_Neg($node, 1);
+
+        return array(
+            array($node, '(-1)'),
+        );
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/Unary/NotTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Unary/NotTest.php
new file mode 100644 (file)
index 0000000..625c252
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_Expression_Unary_NotTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Expression_Unary_Not::__construct
+     */
+    public function testConstructor()
+    {
+        $expr = new Twig_Node_Expression_Constant(1, 1);
+        $node = new Twig_Node_Expression_Unary_Not($expr, 1);
+
+        $this->assertEquals($expr, $node->getNode('node'));
+    }
+
+    /**
+     * @covers Twig_Node_Expression_Unary_Not::compile
+     * @covers Twig_Node_Expression_Unary_Not::operator
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $node = new Twig_Node_Expression_Constant(1, 1);
+        $node = new Twig_Node_Expression_Unary_Not($node, 1);
+
+        return array(
+            array($node, '(!1)'),
+        );
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/Unary/PosTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Unary/PosTest.php
new file mode 100644 (file)
index 0000000..047a097
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_Expression_Unary_PosTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Expression_Unary_Pos::__construct
+     */
+    public function testConstructor()
+    {
+        $expr = new Twig_Node_Expression_Constant(1, 1);
+        $node = new Twig_Node_Expression_Unary_Pos($expr, 1);
+
+        $this->assertEquals($expr, $node->getNode('node'));
+    }
+
+    /**
+     * @covers Twig_Node_Expression_Unary_Pos::compile
+     * @covers Twig_Node_Expression_Unary_Pos::operator
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $node = new Twig_Node_Expression_Constant(1, 1);
+        $node = new Twig_Node_Expression_Unary_Pos($node, 1);
+
+        return array(
+            array($node, '(+1)'),
+        );
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/ForTest.php b/vendor/twig/twig/test/Twig/Tests/Node/ForTest.php
new file mode 100644 (file)
index 0000000..21cc84e
--- /dev/null
@@ -0,0 +1,203 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_ForTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_For::__construct
+     */
+    public function testConstructor()
+    {
+        $keyTarget = new Twig_Node_Expression_AssignName('key', 1);
+        $valueTarget = new Twig_Node_Expression_AssignName('item', 1);
+        $seq = new Twig_Node_Expression_Name('items', 1);
+        $ifexpr = new Twig_Node_Expression_Constant(true, 1);
+        $body = new Twig_Node(array(new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 1), 1)), array(), 1);
+        $else = null;
+        $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, 1);
+        $node->setAttribute('with_loop', false);
+
+        $this->assertEquals($keyTarget, $node->getNode('key_target'));
+        $this->assertEquals($valueTarget, $node->getNode('value_target'));
+        $this->assertEquals($seq, $node->getNode('seq'));
+        $this->assertTrue($node->getAttribute('ifexpr'));
+        $this->assertEquals('Twig_Node_If', get_class($node->getNode('body')));
+        $this->assertEquals($body, $node->getNode('body')->getNode('tests')->getNode(1)->getNode(0));
+        $this->assertEquals(null, $node->getNode('else'));
+
+        $else = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 1), 1);
+        $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, 1);
+        $node->setAttribute('with_loop', false);
+        $this->assertEquals($else, $node->getNode('else'));
+    }
+
+    /**
+     * @covers Twig_Node_For::compile
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $tests = array();
+
+        $keyTarget = new Twig_Node_Expression_AssignName('key', 1);
+        $valueTarget = new Twig_Node_Expression_AssignName('item', 1);
+        $seq = new Twig_Node_Expression_Name('items', 1);
+        $ifexpr = null;
+        $body = new Twig_Node(array(new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 1), 1)), array(), 1);
+        $else = null;
+        $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, 1);
+        $node->setAttribute('with_loop', false);
+
+        $tests[] = array($node, <<<EOF
+// line 1
+\$context['_parent'] = (array) \$context;
+\$context['_seq'] = twig_ensure_traversable({$this->getVariableGetter('items')});
+foreach (\$context['_seq'] as \$context["key"] => \$context["item"]) {
+    echo {$this->getVariableGetter('foo')};
+}
+\$_parent = \$context['_parent'];
+unset(\$context['_seq'], \$context['_iterated'], \$context['key'], \$context['item'], \$context['_parent'], \$context['loop']);
+\$context = array_intersect_key(\$context, \$_parent) + \$_parent;
+EOF
+        );
+
+        $keyTarget = new Twig_Node_Expression_AssignName('k', 1);
+        $valueTarget = new Twig_Node_Expression_AssignName('v', 1);
+        $seq = new Twig_Node_Expression_Name('values', 1);
+        $ifexpr = null;
+        $body = new Twig_Node(array(new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 1), 1)), array(), 1);
+        $else = null;
+        $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, 1);
+        $node->setAttribute('with_loop', true);
+
+        $tests[] = array($node, <<<EOF
+// line 1
+\$context['_parent'] = (array) \$context;
+\$context['_seq'] = twig_ensure_traversable({$this->getVariableGetter('values')});
+\$context['loop'] = array(
+  'parent' => \$context['_parent'],
+  'index0' => 0,
+  'index'  => 1,
+  'first'  => true,
+);
+if (is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_seq'] instanceof Countable)) {
+    \$length = count(\$context['_seq']);
+    \$context['loop']['revindex0'] = \$length - 1;
+    \$context['loop']['revindex'] = \$length;
+    \$context['loop']['length'] = \$length;
+    \$context['loop']['last'] = 1 === \$length;
+}
+foreach (\$context['_seq'] as \$context["k"] => \$context["v"]) {
+    echo {$this->getVariableGetter('foo')};
+    ++\$context['loop']['index0'];
+    ++\$context['loop']['index'];
+    \$context['loop']['first'] = false;
+    if (isset(\$context['loop']['length'])) {
+        --\$context['loop']['revindex0'];
+        --\$context['loop']['revindex'];
+        \$context['loop']['last'] = 0 === \$context['loop']['revindex0'];
+    }
+}
+\$_parent = \$context['_parent'];
+unset(\$context['_seq'], \$context['_iterated'], \$context['k'], \$context['v'], \$context['_parent'], \$context['loop']);
+\$context = array_intersect_key(\$context, \$_parent) + \$_parent;
+EOF
+        );
+
+        $keyTarget = new Twig_Node_Expression_AssignName('k', 1);
+        $valueTarget = new Twig_Node_Expression_AssignName('v', 1);
+        $seq = new Twig_Node_Expression_Name('values', 1);
+        $ifexpr = new Twig_Node_Expression_Constant(true, 1);
+        $body = new Twig_Node(array(new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 1), 1)), array(), 1);
+        $else = null;
+        $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, 1);
+        $node->setAttribute('with_loop', true);
+
+        $tests[] = array($node, <<<EOF
+// line 1
+\$context['_parent'] = (array) \$context;
+\$context['_seq'] = twig_ensure_traversable({$this->getVariableGetter('values')});
+\$context['loop'] = array(
+  'parent' => \$context['_parent'],
+  'index0' => 0,
+  'index'  => 1,
+  'first'  => true,
+);
+foreach (\$context['_seq'] as \$context["k"] => \$context["v"]) {
+    if (true) {
+        echo {$this->getVariableGetter('foo')};
+        ++\$context['loop']['index0'];
+        ++\$context['loop']['index'];
+        \$context['loop']['first'] = false;
+    }
+}
+\$_parent = \$context['_parent'];
+unset(\$context['_seq'], \$context['_iterated'], \$context['k'], \$context['v'], \$context['_parent'], \$context['loop']);
+\$context = array_intersect_key(\$context, \$_parent) + \$_parent;
+EOF
+        );
+
+        $keyTarget = new Twig_Node_Expression_AssignName('k', 1);
+        $valueTarget = new Twig_Node_Expression_AssignName('v', 1);
+        $seq = new Twig_Node_Expression_Name('values', 1);
+        $ifexpr = null;
+        $body = new Twig_Node(array(new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 1), 1)), array(), 1);
+        $else = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 1), 1);
+        $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, 1);
+        $node->setAttribute('with_loop', true);
+
+        $tests[] = array($node, <<<EOF
+// line 1
+\$context['_parent'] = (array) \$context;
+\$context['_seq'] = twig_ensure_traversable({$this->getVariableGetter('values')});
+\$context['_iterated'] = false;
+\$context['loop'] = array(
+  'parent' => \$context['_parent'],
+  'index0' => 0,
+  'index'  => 1,
+  'first'  => true,
+);
+if (is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_seq'] instanceof Countable)) {
+    \$length = count(\$context['_seq']);
+    \$context['loop']['revindex0'] = \$length - 1;
+    \$context['loop']['revindex'] = \$length;
+    \$context['loop']['length'] = \$length;
+    \$context['loop']['last'] = 1 === \$length;
+}
+foreach (\$context['_seq'] as \$context["k"] => \$context["v"]) {
+    echo {$this->getVariableGetter('foo')};
+    \$context['_iterated'] = true;
+    ++\$context['loop']['index0'];
+    ++\$context['loop']['index'];
+    \$context['loop']['first'] = false;
+    if (isset(\$context['loop']['length'])) {
+        --\$context['loop']['revindex0'];
+        --\$context['loop']['revindex'];
+        \$context['loop']['last'] = 0 === \$context['loop']['revindex0'];
+    }
+}
+if (!\$context['_iterated']) {
+    echo {$this->getVariableGetter('foo')};
+}
+\$_parent = \$context['_parent'];
+unset(\$context['_seq'], \$context['_iterated'], \$context['k'], \$context['v'], \$context['_parent'], \$context['loop']);
+\$context = array_intersect_key(\$context, \$_parent) + \$_parent;
+EOF
+        );
+
+        return $tests;
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/IfTest.php b/vendor/twig/twig/test/Twig/Tests/Node/IfTest.php
new file mode 100644 (file)
index 0000000..92fc29d
--- /dev/null
@@ -0,0 +1,100 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_IfTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_If::__construct
+     */
+    public function testConstructor()
+    {
+        $t = new Twig_Node(array(
+            new Twig_Node_Expression_Constant(true, 1),
+            new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 1), 1),
+        ), array(), 1);
+        $else = null;
+        $node = new Twig_Node_If($t, $else, 1);
+
+        $this->assertEquals($t, $node->getNode('tests'));
+        $this->assertEquals(null, $node->getNode('else'));
+
+        $else = new Twig_Node_Print(new Twig_Node_Expression_Name('bar', 1), 1);
+        $node = new Twig_Node_If($t, $else, 1);
+        $this->assertEquals($else, $node->getNode('else'));
+    }
+
+    /**
+     * @covers Twig_Node_If::compile
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $tests = array();
+
+        $t = new Twig_Node(array(
+            new Twig_Node_Expression_Constant(true, 1),
+            new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 1), 1),
+        ), array(), 1);
+        $else = null;
+        $node = new Twig_Node_If($t, $else, 1);
+
+        $tests[] = array($node, <<<EOF
+// line 1
+if (true) {
+    echo {$this->getVariableGetter('foo')};
+}
+EOF
+        );
+
+        $t = new Twig_Node(array(
+            new Twig_Node_Expression_Constant(true, 1),
+            new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 1), 1),
+            new Twig_Node_Expression_Constant(false, 1),
+            new Twig_Node_Print(new Twig_Node_Expression_Name('bar', 1), 1),
+        ), array(), 1);
+        $else = null;
+        $node = new Twig_Node_If($t, $else, 1);
+
+        $tests[] = array($node, <<<EOF
+// line 1
+if (true) {
+    echo {$this->getVariableGetter('foo')};
+} elseif (false) {
+    echo {$this->getVariableGetter('bar')};
+}
+EOF
+        );
+
+        $t = new Twig_Node(array(
+            new Twig_Node_Expression_Constant(true, 1),
+            new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 1), 1),
+        ), array(), 1);
+        $else = new Twig_Node_Print(new Twig_Node_Expression_Name('bar', 1), 1);
+        $node = new Twig_Node_If($t, $else, 1);
+
+        $tests[] = array($node, <<<EOF
+// line 1
+if (true) {
+    echo {$this->getVariableGetter('foo')};
+} else {
+    echo {$this->getVariableGetter('bar')};
+}
+EOF
+        );
+
+        return $tests;
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/ImportTest.php b/vendor/twig/twig/test/Twig/Tests/Node/ImportTest.php
new file mode 100644 (file)
index 0000000..db36581
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_ImportTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Import::__construct
+     */
+    public function testConstructor()
+    {
+        $macro = new Twig_Node_Expression_Constant('foo.twig', 1);
+        $var = new Twig_Node_Expression_AssignName('macro', 1);
+        $node = new Twig_Node_Import($macro, $var, 1);
+
+        $this->assertEquals($macro, $node->getNode('expr'));
+        $this->assertEquals($var, $node->getNode('var'));
+    }
+
+    /**
+     * @covers Twig_Node_Import::compile
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $tests = array();
+
+        $macro = new Twig_Node_Expression_Constant('foo.twig', 1);
+        $var = new Twig_Node_Expression_AssignName('macro', 1);
+        $node = new Twig_Node_Import($macro, $var, 1);
+
+        $tests[] = array($node, <<<EOF
+// line 1
+\$context["macro"] = \$this->env->loadTemplate("foo.twig");
+EOF
+        );
+
+        return $tests;
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/IncludeTest.php b/vendor/twig/twig/test/Twig/Tests/Node/IncludeTest.php
new file mode 100644 (file)
index 0000000..3b7da6e
--- /dev/null
@@ -0,0 +1,96 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_IncludeTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Include::__construct
+     */
+    public function testConstructor()
+    {
+        $expr = new Twig_Node_Expression_Constant('foo.twig', 1);
+        $node = new Twig_Node_Include($expr, null, false, false, 1);
+
+        $this->assertEquals(null, $node->getNode('variables'));
+        $this->assertEquals($expr, $node->getNode('expr'));
+        $this->assertFalse($node->getAttribute('only'));
+
+        $vars = new Twig_Node_Expression_Array(array(new Twig_Node_Expression_Constant('foo', 1), new Twig_Node_Expression_Constant(true, 1)), 1);
+        $node = new Twig_Node_Include($expr, $vars, true, false, 1);
+        $this->assertEquals($vars, $node->getNode('variables'));
+        $this->assertTrue($node->getAttribute('only'));
+    }
+
+    /**
+     * @covers Twig_Node_Include::compile
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $tests = array();
+
+        $expr = new Twig_Node_Expression_Constant('foo.twig', 1);
+        $node = new Twig_Node_Include($expr, null, false, false, 1);
+        $tests[] = array($node, <<<EOF
+// line 1
+\$this->env->loadTemplate("foo.twig")->display(\$context);
+EOF
+        );
+
+        $expr = new Twig_Node_Expression_Conditional(
+                        new Twig_Node_Expression_Constant(true, 1),
+                        new Twig_Node_Expression_Constant('foo', 1),
+                        new Twig_Node_Expression_Constant('foo', 1),
+                        0
+                    );
+        $node = new Twig_Node_Include($expr, null, false, false, 1);
+        $tests[] = array($node, <<<EOF
+// line 1
+\$template = \$this->env->resolveTemplate(((true) ? ("foo") : ("foo")));
+\$template->display(\$context);
+EOF
+        );
+
+        $expr = new Twig_Node_Expression_Constant('foo.twig', 1);
+        $vars = new Twig_Node_Expression_Array(array(new Twig_Node_Expression_Constant('foo', 1), new Twig_Node_Expression_Constant(true, 1)), 1);
+        $node = new Twig_Node_Include($expr, $vars, false, false, 1);
+        $tests[] = array($node, <<<EOF
+// line 1
+\$this->env->loadTemplate("foo.twig")->display(array_merge(\$context, array("foo" => true)));
+EOF
+        );
+
+        $node = new Twig_Node_Include($expr, $vars, true, false, 1);
+        $tests[] = array($node, <<<EOF
+// line 1
+\$this->env->loadTemplate("foo.twig")->display(array("foo" => true));
+EOF
+        );
+
+        $node = new Twig_Node_Include($expr, $vars, true, true, 1);
+        $tests[] = array($node, <<<EOF
+// line 1
+try {
+    \$this->env->loadTemplate("foo.twig")->display(array("foo" => true));
+} catch (Twig_Error_Loader \$e) {
+    // ignore missing template
+}
+EOF
+        );
+
+        return $tests;
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/MacroTest.php b/vendor/twig/twig/test/Twig/Tests/Node/MacroTest.php
new file mode 100644 (file)
index 0000000..4d2f641
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_MacroTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Macro::__construct
+     */
+    public function testConstructor()
+    {
+        $body = new Twig_Node_Text('foo', 1);
+        $arguments = new Twig_Node(array(new Twig_Node_Expression_Name('foo', 1)), array(), 1);
+        $node = new Twig_Node_Macro('foo', $body, $arguments, 1);
+
+        $this->assertEquals($body, $node->getNode('body'));
+        $this->assertEquals($arguments, $node->getNode('arguments'));
+        $this->assertEquals('foo', $node->getAttribute('name'));
+    }
+
+    /**
+     * @covers Twig_Node_Macro::compile
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $body = new Twig_Node_Text('foo', 1);
+        $arguments = new Twig_Node(array(
+            'foo' => new Twig_Node_Expression_Constant(null, 1),
+            'bar' => new Twig_Node_Expression_Constant('Foo', 1),
+        ), array(), 1);
+        $node = new Twig_Node_Macro('foo', $body, $arguments, 1);
+
+        return array(
+            array($node, <<<EOF
+// line 1
+public function getfoo(\$_foo = null, \$_bar = "Foo")
+{
+    \$context = \$this->env->mergeGlobals(array(
+        "foo" => \$_foo,
+        "bar" => \$_bar,
+    ));
+
+    \$blocks = array();
+
+    ob_start();
+    try {
+        echo "foo";
+    } catch (Exception \$e) {
+        ob_end_clean();
+
+        throw \$e;
+    }
+
+    return ('' === \$tmp = ob_get_clean()) ? '' : new Twig_Markup(\$tmp, \$this->env->getCharset());
+}
+EOF
+            ),
+        );
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/ModuleTest.php b/vendor/twig/twig/test/Twig/Tests/Node/ModuleTest.php
new file mode 100644 (file)
index 0000000..9411e99
--- /dev/null
@@ -0,0 +1,196 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_ModuleTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Module::__construct
+     */
+    public function testConstructor()
+    {
+        $body = new Twig_Node_Text('foo', 1);
+        $parent = new Twig_Node_Expression_Constant('layout.twig', 1);
+        $blocks = new Twig_Node();
+        $macros = new Twig_Node();
+        $traits = new Twig_Node();
+        $filename = 'foo.twig';
+        $node = new Twig_Node_Module($body, $parent, $blocks, $macros, $traits, new Twig_Node(array()), $filename);
+
+        $this->assertEquals($body, $node->getNode('body'));
+        $this->assertEquals($blocks, $node->getNode('blocks'));
+        $this->assertEquals($macros, $node->getNode('macros'));
+        $this->assertEquals($parent, $node->getNode('parent'));
+        $this->assertEquals($filename, $node->getAttribute('filename'));
+    }
+
+    /**
+     * @covers Twig_Node_Module::compile
+     * @covers Twig_Node_Module::compileTemplate
+     * @covers Twig_Node_Module::compileMacros
+     * @covers Twig_Node_Module::compileClassHeader
+     * @covers Twig_Node_Module::compileDisplayHeader
+     * @covers Twig_Node_Module::compileDisplayBody
+     * @covers Twig_Node_Module::compileDisplayFooter
+     * @covers Twig_Node_Module::compileClassFooter
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $twig = new Twig_Environment(new Twig_Loader_String());
+
+        $tests = array();
+
+        $body = new Twig_Node_Text('foo', 1);
+        $extends = null;
+        $blocks = new Twig_Node();
+        $macros = new Twig_Node();
+        $traits = new Twig_Node();
+        $filename = 'foo.twig';
+
+        $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, new Twig_Node(array()), $filename);
+        $tests[] = array($node, <<<EOF
+<?php
+
+/* foo.twig */
+class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends Twig_Template
+{
+    public function __construct(Twig_Environment \$env)
+    {
+        parent::__construct(\$env);
+
+        \$this->parent = false;
+
+        \$this->blocks = array(
+        );
+    }
+
+    protected function doDisplay(array \$context, array \$blocks = array())
+    {
+        // line 1
+        echo "foo";
+    }
+
+    public function getTemplateName()
+    {
+        return "foo.twig";
+    }
+
+    public function getDebugInfo()
+    {
+        return array (  19 => 1,);
+    }
+}
+EOF
+        , $twig);
+
+        $import = new Twig_Node_Import(new Twig_Node_Expression_Constant('foo.twig', 1), new Twig_Node_Expression_AssignName('macro', 1), 1);
+
+        $body = new Twig_Node(array($import));
+        $extends = new Twig_Node_Expression_Constant('layout.twig', 1);
+
+        $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, new Twig_Node(array()), $filename);
+        $tests[] = array($node, <<<EOF
+<?php
+
+/* foo.twig */
+class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends Twig_Template
+{
+    public function __construct(Twig_Environment \$env)
+    {
+        parent::__construct(\$env);
+
+        \$this->parent = \$this->env->loadTemplate("layout.twig");
+
+        \$this->blocks = array(
+        );
+    }
+
+    protected function doGetParent(array \$context)
+    {
+        return "layout.twig";
+    }
+
+    protected function doDisplay(array \$context, array \$blocks = array())
+    {
+        // line 1
+        \$context["macro"] = \$this->env->loadTemplate("foo.twig");
+        \$this->parent->display(\$context, array_merge(\$this->blocks, \$blocks));
+    }
+
+    public function getTemplateName()
+    {
+        return "foo.twig";
+    }
+
+    public function isTraitable()
+    {
+        return false;
+    }
+
+    public function getDebugInfo()
+    {
+        return array (  24 => 1,);
+    }
+}
+EOF
+        , $twig);
+
+        $body = new Twig_Node();
+        $extends = new Twig_Node_Expression_Conditional(
+                        new Twig_Node_Expression_Constant(true, 1),
+                        new Twig_Node_Expression_Constant('foo', 1),
+                        new Twig_Node_Expression_Constant('foo', 1),
+                        0
+                    );
+
+        $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, new Twig_Node(array()), $filename);
+        $tests[] = array($node, <<<EOF
+<?php
+
+/* foo.twig */
+class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends Twig_Template
+{
+    protected function doGetParent(array \$context)
+    {
+        return \$this->env->resolveTemplate(((true) ? ("foo") : ("foo")));
+    }
+
+    protected function doDisplay(array \$context, array \$blocks = array())
+    {
+        \$this->getParent(\$context)->display(\$context, array_merge(\$this->blocks, \$blocks));
+    }
+
+    public function getTemplateName()
+    {
+        return "foo.twig";
+    }
+
+    public function isTraitable()
+    {
+        return false;
+    }
+
+    public function getDebugInfo()
+    {
+        return array ();
+    }
+}
+EOF
+        , $twig);
+
+        return $tests;
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/PrintTest.php b/vendor/twig/twig/test/Twig/Tests/Node/PrintTest.php
new file mode 100644 (file)
index 0000000..6fe43a4
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_PrintTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Print::__construct
+     */
+    public function testConstructor()
+    {
+        $expr = new Twig_Node_Expression_Constant('foo', 1);
+        $node = new Twig_Node_Print($expr, 1);
+
+        $this->assertEquals($expr, $node->getNode('expr'));
+    }
+
+    /**
+     * @covers Twig_Node_Print::compile
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $tests = array();
+        $tests[] = array(new Twig_Node_Print(new Twig_Node_Expression_Constant('foo', 1), 1), "// line 1\necho \"foo\";");
+
+        return $tests;
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/SandboxTest.php b/vendor/twig/twig/test/Twig/Tests/Node/SandboxTest.php
new file mode 100644 (file)
index 0000000..db9dbf9
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_SandboxTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Sandbox::__construct
+     */
+    public function testConstructor()
+    {
+        $body = new Twig_Node_Text('foo', 1);
+        $node = new Twig_Node_Sandbox($body, 1);
+
+        $this->assertEquals($body, $node->getNode('body'));
+    }
+
+    /**
+     * @covers Twig_Node_Sandbox::compile
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $tests = array();
+
+        $body = new Twig_Node_Text('foo', 1);
+        $node = new Twig_Node_Sandbox($body, 1);
+
+        $tests[] = array($node, <<<EOF
+// line 1
+\$sandbox = \$this->env->getExtension('sandbox');
+if (!\$alreadySandboxed = \$sandbox->isSandboxed()) {
+    \$sandbox->enableSandbox();
+}
+echo "foo";
+if (!\$alreadySandboxed) {
+    \$sandbox->disableSandbox();
+}
+EOF
+        );
+
+        return $tests;
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/SandboxedModuleTest.php b/vendor/twig/twig/test/Twig/Tests/Node/SandboxedModuleTest.php
new file mode 100644 (file)
index 0000000..217e340
--- /dev/null
@@ -0,0 +1,173 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_SandboxedModuleTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_SandboxedModule::__construct
+     */
+    public function testConstructor()
+    {
+        $body = new Twig_Node_Text('foo', 1);
+        $parent = new Twig_Node_Expression_Constant('layout.twig', 1);
+        $blocks = new Twig_Node();
+        $macros = new Twig_Node();
+        $traits = new Twig_Node();
+        $filename = 'foo.twig';
+        $node = new Twig_Node_Module($body, $parent, $blocks, $macros, $traits, new Twig_Node(array()), $filename);
+        $node = new Twig_Node_SandboxedModule($node, array('for'), array('upper'), array('cycle'));
+
+        $this->assertEquals($body, $node->getNode('body'));
+        $this->assertEquals($blocks, $node->getNode('blocks'));
+        $this->assertEquals($macros, $node->getNode('macros'));
+        $this->assertEquals($parent, $node->getNode('parent'));
+        $this->assertEquals($filename, $node->getAttribute('filename'));
+    }
+
+    /**
+     * @covers Twig_Node_SandboxedModule::compile
+     * @covers Twig_Node_SandboxedModule::compileDisplayBody
+     * @covers Twig_Node_SandboxedModule::compileDisplayFooter
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $twig = new Twig_Environment(new Twig_Loader_String());
+
+        $tests = array();
+
+        $body = new Twig_Node_Text('foo', 1);
+        $extends = null;
+        $blocks = new Twig_Node();
+        $macros = new Twig_Node();
+        $traits = new Twig_Node();
+        $filename = 'foo.twig';
+
+        $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, new Twig_Node(array()), $filename);
+        $node = new Twig_Node_SandboxedModule($node, array('for'), array('upper'), array('cycle'));
+
+        $tests[] = array($node, <<<EOF
+<?php
+
+/* foo.twig */
+class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends Twig_Template
+{
+    public function __construct(Twig_Environment \$env)
+    {
+        parent::__construct(\$env);
+
+        \$this->parent = false;
+
+        \$this->blocks = array(
+        );
+    }
+
+    protected function doDisplay(array \$context, array \$blocks = array())
+    {
+        \$this->checkSecurity();
+        // line 1
+        echo "foo";
+    }
+
+    protected function checkSecurity()
+    {
+        \$this->env->getExtension('sandbox')->checkSecurity(
+            array('upper'),
+            array('for'),
+            array('cycle')
+        );
+    }
+
+    public function getTemplateName()
+    {
+        return "foo.twig";
+    }
+
+    public function getDebugInfo()
+    {
+        return array (  20 => 1,);
+    }
+}
+EOF
+        , $twig);
+
+        $body = new Twig_Node();
+        $extends = new Twig_Node_Expression_Constant('layout.twig', 1);
+        $blocks = new Twig_Node();
+        $macros = new Twig_Node();
+        $traits = new Twig_Node();
+        $filename = 'foo.twig';
+
+        $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, new Twig_Node(array()), $filename);
+        $node = new Twig_Node_SandboxedModule($node, array('for'), array('upper'), array('cycle'));
+
+        $tests[] = array($node, <<<EOF
+<?php
+
+/* foo.twig */
+class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends Twig_Template
+{
+    public function __construct(Twig_Environment \$env)
+    {
+        parent::__construct(\$env);
+
+        \$this->parent = \$this->env->loadTemplate("layout.twig");
+
+        \$this->blocks = array(
+        );
+    }
+
+    protected function doGetParent(array \$context)
+    {
+        return "layout.twig";
+    }
+
+    protected function doDisplay(array \$context, array \$blocks = array())
+    {
+        \$this->checkSecurity();
+        \$this->parent->display(\$context, array_merge(\$this->blocks, \$blocks));
+    }
+
+    protected function checkSecurity()
+    {
+        \$this->env->getExtension('sandbox')->checkSecurity(
+            array('upper'),
+            array('for'),
+            array('cycle')
+        );
+    }
+
+    public function getTemplateName()
+    {
+        return "foo.twig";
+    }
+
+    public function isTraitable()
+    {
+        return false;
+    }
+
+    public function getDebugInfo()
+    {
+        return array ();
+    }
+}
+EOF
+        , $twig);
+
+        return $tests;
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/SandboxedPrintTest.php b/vendor/twig/twig/test/Twig/Tests/Node/SandboxedPrintTest.php
new file mode 100644 (file)
index 0000000..058e02b
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_SandboxedPrintTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_SandboxedPrint::__construct
+     */
+    public function testConstructor()
+    {
+        $node = new Twig_Node_SandboxedPrint($expr = new Twig_Node_Expression_Constant('foo', 1), 1);
+
+        $this->assertEquals($expr, $node->getNode('expr'));
+    }
+
+    /**
+     * @covers Twig_Node_SandboxedPrint::compile
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $tests = array();
+
+        $tests[] = array(new Twig_Node_SandboxedPrint(new Twig_Node_Expression_Constant('foo', 1), 1), <<<EOF
+// line 1
+echo \$this->env->getExtension('sandbox')->ensureToStringAllowed("foo");
+EOF
+        );
+
+        return $tests;
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/SetTest.php b/vendor/twig/twig/test/Twig/Tests/Node/SetTest.php
new file mode 100644 (file)
index 0000000..d64d671
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_SetTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Set::__construct
+     */
+    public function testConstructor()
+    {
+        $names = new Twig_Node(array(new Twig_Node_Expression_AssignName('foo', 1)), array(), 1);
+        $values = new Twig_Node(array(new Twig_Node_Expression_Constant('foo', 1)), array(), 1);
+        $node = new Twig_Node_Set(false, $names, $values, 1);
+
+        $this->assertEquals($names, $node->getNode('names'));
+        $this->assertEquals($values, $node->getNode('values'));
+        $this->assertEquals(false, $node->getAttribute('capture'));
+    }
+
+    /**
+     * @covers Twig_Node_Set::compile
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $tests = array();
+
+        $names = new Twig_Node(array(new Twig_Node_Expression_AssignName('foo', 1)), array(), 1);
+        $values = new Twig_Node(array(new Twig_Node_Expression_Constant('foo', 1)), array(), 1);
+        $node = new Twig_Node_Set(false, $names, $values, 1);
+        $tests[] = array($node, <<<EOF
+// line 1
+\$context["foo"] = "foo";
+EOF
+        );
+
+        $names = new Twig_Node(array(new Twig_Node_Expression_AssignName('foo', 1)), array(), 1);
+        $values = new Twig_Node(array(new Twig_Node_Print(new Twig_Node_Expression_Constant('foo', 1), 1)), array(), 1);
+        $node = new Twig_Node_Set(true, $names, $values, 1);
+        $tests[] = array($node, <<<EOF
+// line 1
+ob_start();
+echo "foo";
+\$context["foo"] = ('' === \$tmp = ob_get_clean()) ? '' : new Twig_Markup(\$tmp, \$this->env->getCharset());
+EOF
+        );
+
+        $names = new Twig_Node(array(new Twig_Node_Expression_AssignName('foo', 1)), array(), 1);
+        $values = new Twig_Node_Text('foo', 1);
+        $node = new Twig_Node_Set(true, $names, $values, 1);
+        $tests[] = array($node, <<<EOF
+// line 1
+\$context["foo"] = ('' === \$tmp = "foo") ? '' : new Twig_Markup(\$tmp, \$this->env->getCharset());
+EOF
+        );
+
+        $names = new Twig_Node(array(new Twig_Node_Expression_AssignName('foo', 1), new Twig_Node_Expression_AssignName('bar', 1)), array(), 1);
+        $values = new Twig_Node(array(new Twig_Node_Expression_Constant('foo', 1), new Twig_Node_Expression_Name('bar', 1)), array(), 1);
+        $node = new Twig_Node_Set(false, $names, $values, 1);
+        $tests[] = array($node, <<<EOF
+// line 1
+list(\$context["foo"], \$context["bar"]) = array("foo", {$this->getVariableGetter('bar')});
+EOF
+        );
+
+        return $tests;
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/SpacelessTest.php b/vendor/twig/twig/test/Twig/Tests/Node/SpacelessTest.php
new file mode 100644 (file)
index 0000000..6735dc3
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_SpacelessTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Spaceless::__construct
+     */
+    public function testConstructor()
+    {
+        $body = new Twig_Node(array(new Twig_Node_Text('<div>   <div>   foo   </div>   </div>', 1)));
+        $node = new Twig_Node_Spaceless($body, 1);
+
+        $this->assertEquals($body, $node->getNode('body'));
+    }
+
+    /**
+     * @covers Twig_Node_Spaceless::compile
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $body = new Twig_Node(array(new Twig_Node_Text('<div>   <div>   foo   </div>   </div>', 1)));
+        $node = new Twig_Node_Spaceless($body, 1);
+
+        return array(
+            array($node, <<<EOF
+// line 1
+ob_start();
+echo "<div>   <div>   foo   </div>   </div>";
+echo trim(preg_replace('/>\s+</', '><', ob_get_clean()));
+EOF
+            ),
+        );
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/TextTest.php b/vendor/twig/twig/test/Twig/Tests/Node/TextTest.php
new file mode 100644 (file)
index 0000000..6f85576
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_Node_TextTest extends Twig_Test_NodeTestCase
+{
+    /**
+     * @covers Twig_Node_Text::__construct
+     */
+    public function testConstructor()
+    {
+        $node = new Twig_Node_Text('foo', 1);
+
+        $this->assertEquals('foo', $node->getAttribute('data'));
+    }
+
+    /**
+     * @covers Twig_Node_Text::compile
+     * @dataProvider getTests
+     */
+    public function testCompile($node, $source, $environment = null)
+    {
+        parent::testCompile($node, $source, $environment);
+    }
+
+    public function getTests()
+    {
+        $tests = array();
+        $tests[] = array(new Twig_Node_Text('foo', 1), "// line 1\necho \"foo\";");
+
+        return $tests;
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/NodeVisitor/OptimizerTest.php b/vendor/twig/twig/test/Twig/Tests/NodeVisitor/OptimizerTest.php
new file mode 100644 (file)
index 0000000..d35740d
--- /dev/null
@@ -0,0 +1,114 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+class Twig_Tests_NodeVisitor_OptimizerTest extends PHPUnit_Framework_TestCase
+{
+    public function testRenderBlockOptimizer()
+    {
+        $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
+
+        $stream = $env->parse($env->tokenize('{{ block("foo") }}', 'index'));
+
+        $node = $stream->getNode('body')->getNode(0);
+
+        $this->assertEquals('Twig_Node_Expression_BlockReference', get_class($node));
+        $this->assertTrue($node->getAttribute('output'));
+    }
+
+    public function testRenderParentBlockOptimizer()
+    {
+        $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
+
+        $stream = $env->parse($env->tokenize('{% extends "foo" %}{% block content %}{{ parent() }}{% endblock %}', 'index'));
+
+        $node = $stream->getNode('blocks')->getNode('content')->getNode(0)->getNode('body');
+
+        $this->assertEquals('Twig_Node_Expression_Parent', get_class($node));
+        $this->assertTrue($node->getAttribute('output'));
+    }
+
+    public function testRenderVariableBlockOptimizer()
+    {
+        if (version_compare(phpversion(), '5.4.0RC1', '>=')) {
+            return;
+        }
+
+        $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
+        $stream = $env->parse($env->tokenize('{{ block(name|lower) }}', 'index'));
+
+        $node = $stream->getNode('body')->getNode(0)->getNode(1);
+
+        $this->assertEquals('Twig_Node_Expression_BlockReference', get_class($node));
+        $this->assertTrue($node->getAttribute('output'));
+    }
+
+    /**
+     * @dataProvider getTestsForForOptimizer
+     */
+    public function testForOptimizer($template, $expected)
+    {
+        $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false));
+
+        $stream = $env->parse($env->tokenize($template, 'index'));
+
+        foreach ($expected as $target => $withLoop) {
+            $this->assertTrue($this->checkForConfiguration($stream, $target, $withLoop), sprintf('variable %s is %soptimized', $target, $withLoop ? 'not ' : ''));
+        }
+    }
+
+    public function getTestsForForOptimizer()
+    {
+        return array(
+            array('{% for i in foo %}{% endfor %}', array('i' => false)),
+
+            array('{% for i in foo %}{{ loop.index }}{% endfor %}', array('i' => true)),
+
+            array('{% for i in foo %}{% for j in foo %}{% endfor %}{% endfor %}', array('i' => false, 'j' => false)),
+
+            array('{% for i in foo %}{% include "foo" %}{% endfor %}', array('i' => true)),
+
+            array('{% for i in foo %}{% include "foo" only %}{% endfor %}', array('i' => false)),
+
+            array('{% for i in foo %}{% include "foo" with { "foo": "bar" } only %}{% endfor %}', array('i' => false)),
+
+            array('{% for i in foo %}{% include "foo" with { "foo": loop.index } only %}{% endfor %}', array('i' => true)),
+
+            array('{% for i in foo %}{% for j in foo %}{{ loop.index }}{% endfor %}{% endfor %}', array('i' => false, 'j' => true)),
+
+            array('{% for i in foo %}{% for j in foo %}{{ loop.parent.loop.index }}{% endfor %}{% endfor %}', array('i' => true, 'j' => true)),
+
+            array('{% for i in foo %}{% set l = loop %}{% for j in foo %}{{ l.index }}{% endfor %}{% endfor %}', array('i' => true, 'j' => false)),
+
+            array('{% for i in foo %}{% for j in foo %}{{ foo.parent.loop.index }}{% endfor %}{% endfor %}', array('i' => false, 'j' => false)),
+
+            array('{% for i in foo %}{% for j in foo %}{{ loop["parent"].loop.index }}{% endfor %}{% endfor %}', array('i' => true, 'j' => true)),
+        );
+    }
+
+    public function checkForConfiguration(Twig_NodeInterface $node = null, $target, $withLoop)
+    {
+        if (null === $node) {
+            return;
+        }
+
+        foreach ($node as $n) {
+            if ($n instanceof Twig_Node_For) {
+                if ($target === $n->getNode('value_target')->getAttribute('name')) {
+                    return $withLoop == $n->getAttribute('with_loop');
+                }
+            }
+
+            $ret = $this->checkForConfiguration($n, $target, $withLoop);
+            if (null !== $ret) {
+                return $ret;
+            }
+        }
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/ParserTest.php b/vendor/twig/twig/test/Twig/Tests/ParserTest.php
new file mode 100644 (file)
index 0000000..55eb7fb
--- /dev/null
@@ -0,0 +1,180 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+class Twig_Tests_ParserTest extends PHPUnit_Framework_TestCase
+{
+    /**
+     * @expectedException Twig_Error_Syntax
+     */
+    public function testSetMacroThrowsExceptionOnReservedMethods()
+    {
+        $parser = $this->getParser();
+        $parser->setMacro('display', $this->getMock('Twig_Node_Macro', array(), array(), '', null));
+    }
+
+    /**
+     * @expectedException        Twig_Error_Syntax
+     * @expectedExceptionMessage Unknown tag name "foo". Did you mean "for" at line 1
+     */
+    public function testUnknownTag()
+    {
+        $stream = new Twig_TokenStream(array(
+            new Twig_Token(Twig_Token::BLOCK_START_TYPE, '', 1),
+            new Twig_Token(Twig_Token::NAME_TYPE, 'foo', 1),
+            new Twig_Token(Twig_Token::BLOCK_END_TYPE, '', 1),
+            new Twig_Token(Twig_Token::EOF_TYPE, '', 1),
+        ));
+        $parser = new Twig_Parser(new Twig_Environment());
+        $parser->parse($stream);
+    }
+
+    /**
+     * @dataProvider getFilterBodyNodesData
+     */
+    public function testFilterBodyNodes($input, $expected)
+    {
+        $parser = $this->getParser();
+
+        $this->assertEquals($expected, $parser->filterBodyNodes($input));
+    }
+
+    public function getFilterBodyNodesData()
+    {
+        return array(
+            array(
+                new Twig_Node(array(new Twig_Node_Text('   ', 1))),
+                new Twig_Node(array()),
+            ),
+            array(
+                $input = new Twig_Node(array(new Twig_Node_Set(false, new Twig_Node(), new Twig_Node(), 1))),
+                $input,
+            ),
+            array(
+                $input = new Twig_Node(array(new Twig_Node_Set(true, new Twig_Node(), new Twig_Node(array(new Twig_Node(array(new Twig_Node_Text('foo', 1))))), 1))),
+                $input,
+            ),
+        );
+    }
+
+    /**
+     * @dataProvider getFilterBodyNodesDataThrowsException
+     * @expectedException Twig_Error_Syntax
+     */
+    public function testFilterBodyNodesThrowsException($input)
+    {
+        $parser = $this->getParser();
+
+        $parser->filterBodyNodes($input);
+    }
+
+    public function getFilterBodyNodesDataThrowsException()
+    {
+        return array(
+            array(new Twig_Node_Text('foo', 1)),
+            array(new Twig_Node(array(new Twig_Node(array(new Twig_Node_Text('foo', 1)))))),
+        );
+    }
+
+    /**
+     * @expectedException Twig_Error_Syntax
+     * @expectedExceptionMessage A template that extends another one cannot have a body but a byte order mark (BOM) has been detected; it must be removed at line 1.
+     */
+    public function testFilterBodyNodesWithBOM()
+    {
+        $parser = $this->getParser();
+        $parser->filterBodyNodes(new Twig_Node_Text(chr(0xEF).chr(0xBB).chr(0xBF), 1));
+    }
+
+    public function testParseIsReentrant()
+    {
+        $twig = new Twig_Environment(null, array(
+            'autoescape' => false,
+            'optimizations' => 0,
+        ));
+        $twig->addTokenParser(new TestTokenParser());
+
+        $parser = new Twig_Parser($twig);
+
+        $parser->parse(new Twig_TokenStream(array(
+            new Twig_Token(Twig_Token::BLOCK_START_TYPE, '', 1),
+            new Twig_Token(Twig_Token::NAME_TYPE, 'test', 1),
+            new Twig_Token(Twig_Token::BLOCK_END_TYPE, '', 1),
+            new Twig_Token(Twig_Token::VAR_START_TYPE, '', 1),
+            new Twig_Token(Twig_Token::NAME_TYPE, 'foo', 1),
+            new Twig_Token(Twig_Token::VAR_END_TYPE, '', 1),
+            new Twig_Token(Twig_Token::EOF_TYPE, '', 1),
+        )));
+
+        $this->assertEquals(null, $parser->getParent());
+    }
+
+    // The getVarName() must not depend on the template loaders,
+    // If this test does not throw any exception, that's good.
+    // see https://github.com/symfony/symfony/issues/4218
+    public function testGetVarName()
+    {
+        $twig = new Twig_Environment(null, array(
+            'autoescape' => false,
+            'optimizations' => 0,
+        ));
+
+        $twig->parse($twig->tokenize(<<<EOF
+{% from _self import foo %}
+
+{% macro foo() %}
+    {{ foo }}
+{% endmacro %}
+EOF
+        ));
+    }
+
+    protected function getParser()
+    {
+        $parser = new TestParser(new Twig_Environment());
+        $parser->setParent(new Twig_Node());
+        $parser->stream = $this->getMockBuilder('Twig_TokenStream')->disableOriginalConstructor()->getMock();
+
+        return $parser;
+    }
+}
+
+class TestParser extends Twig_Parser
+{
+    public $stream;
+
+    public function filterBodyNodes(Twig_NodeInterface $node)
+    {
+        return parent::filterBodyNodes($node);
+    }
+}
+
+class TestTokenParser extends Twig_TokenParser
+{
+    public function parse(Twig_Token $token)
+    {
+        // simulate the parsing of another template right in the middle of the parsing of the current template
+        $this->parser->parse(new Twig_TokenStream(array(
+            new Twig_Token(Twig_Token::BLOCK_START_TYPE, '', 1),
+            new Twig_Token(Twig_Token::NAME_TYPE, 'extends', 1),
+            new Twig_Token(Twig_Token::STRING_TYPE, 'base', 1),
+            new Twig_Token(Twig_Token::BLOCK_END_TYPE, '', 1),
+            new Twig_Token(Twig_Token::EOF_TYPE, '', 1),
+        )));
+
+        $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
+
+        return new Twig_Node(array());
+    }
+
+    public function getTag()
+    {
+        return 'test';
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/TemplateTest.php b/vendor/twig/twig/test/Twig/Tests/TemplateTest.php
new file mode 100644 (file)
index 0000000..823a9ce
--- /dev/null
@@ -0,0 +1,626 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase
+{
+    /**
+     * @dataProvider getAttributeExceptions
+     */
+    public function testGetAttributeExceptions($template, $message, $useExt)
+    {
+        $name = 'index_'.($useExt ? 1 : 0);
+        $templates = array(
+            $name => $template.$useExt, // appending $useExt makes the template content unique
+        );
+
+        $env = new Twig_Environment(new Twig_Loader_Array($templates), array('strict_variables' => true));
+        if (!$useExt) {
+            $env->addNodeVisitor(new CExtDisablingNodeVisitor());
+        }
+        $template = $env->loadTemplate($name);
+
+        $context = array(
+            'string'          => 'foo',
+            'array'           => array('foo' => 'foo'),
+            'array_access'    => new Twig_TemplateArrayAccessObject(),
+            'magic_exception' => new Twig_TemplateMagicPropertyObjectWithException(),
+        );
+
+        try {
+            $template->render($context);
+            $this->fail('Accessing an invalid attribute should throw an exception.');
+        } catch (Twig_Error_Runtime $e) {
+            $this->assertSame(sprintf($message, $name), $e->getMessage());
+        }
+    }
+
+    public function getAttributeExceptions()
+    {
+        $tests = array(
+            array('{{ string["a"] }}', 'Impossible to access a key ("a") on a string variable ("foo") in "%s" at line 1', false),
+            array('{{ array["a"] }}', 'Key "a" for array with keys "foo" does not exist in "%s" at line 1', false),
+            array('{{ array_access["a"] }}', 'Key "a" in object (with ArrayAccess) of type "Twig_TemplateArrayAccessObject" does not exist in "%s" at line 1', false),
+            array('{{ string.a }}', 'Impossible to access an attribute ("a") on a string variable ("foo") in "%s" at line 1', false),
+            array('{{ string.a() }}', 'Impossible to invoke a method ("a") on a string variable ("foo") in "%s" at line 1', false),
+            array('{{ array.a }}', 'Key "a" for array with keys "foo" does not exist in "%s" at line 1', false),
+            array('{{ attribute(array, -10) }}', 'Key "-10" for array with keys "foo" does not exist in "%s" at line 1', false),
+            array('{{ array_access.a }}', 'Method "a" for object "Twig_TemplateArrayAccessObject" does not exist in "%s" at line 1', false),
+            array('{% macro foo(obj) %}{{ obj.missing_method() }}{% endmacro %}{{ _self.foo(array_access) }}', 'Method "missing_method" for object "Twig_TemplateArrayAccessObject" does not exist in "%s" at line 1', false),
+            array('{{ magic_exception.test }}', 'An exception has been thrown during the rendering of a template ("Hey! Don\'t try to isset me!") in "%s" at line 1.', false),
+        );
+
+        if (function_exists('twig_template_get_attributes')) {
+            foreach (array_slice($tests, 0) as $test) {
+                $test[2] = true;
+                $tests[] = $test;
+            }
+        }
+
+        return $tests;
+    }
+
+    /**
+     * @dataProvider getGetAttributeWithSandbox
+     */
+    public function testGetAttributeWithSandbox($object, $item, $allowed, $useExt)
+    {
+        $twig = new Twig_Environment();
+        $policy = new Twig_Sandbox_SecurityPolicy(array(), array(), array(/*method*/), array(/*prop*/), array());
+        $twig->addExtension(new Twig_Extension_Sandbox($policy, !$allowed));
+        $template = new Twig_TemplateTest($twig, $useExt);
+
+        try {
+            $template->getAttribute($object, $item, array(), 'any');
+
+            if (!$allowed) {
+                $this->fail();
+            }
+        } catch (Twig_Sandbox_SecurityError $e) {
+            if ($allowed) {
+                $this->fail();
+            }
+
+            $this->assertContains('is not allowed', $e->getMessage());
+        }
+    }
+
+    public function getGetAttributeWithSandbox()
+    {
+        $tests = array(
+            array(new Twig_TemplatePropertyObject(), 'defined', false, false),
+            array(new Twig_TemplatePropertyObject(), 'defined', true, false),
+            array(new Twig_TemplateMethodObject(), 'defined', false, false),
+            array(new Twig_TemplateMethodObject(), 'defined', true, false),
+        );
+
+        if (function_exists('twig_template_get_attributes')) {
+            foreach (array_slice($tests, 0) as $test) {
+                $test[3] = true;
+                $tests[] = $test;
+            }
+        }
+
+        return $tests;
+    }
+
+    /**
+     * @dataProvider getGetAttributeWithTemplateAsObject
+     */
+    public function testGetAttributeWithTemplateAsObject($useExt)
+    {
+        $template = new Twig_TemplateTest(new Twig_Environment(), $useExt);
+        $template1 = new Twig_TemplateTest(new Twig_Environment(), false);
+
+        $this->assertInstanceof('Twig_Markup', $template->getAttribute($template1, 'string'));
+        $this->assertEquals('some_string', $template->getAttribute($template1, 'string'));
+
+        $this->assertInstanceof('Twig_Markup', $template->getAttribute($template1, 'true'));
+        $this->assertEquals('1', $template->getAttribute($template1, 'true'));
+
+        $this->assertInstanceof('Twig_Markup', $template->getAttribute($template1, 'zero'));
+        $this->assertEquals('0', $template->getAttribute($template1, 'zero'));
+
+        $this->assertNotInstanceof('Twig_Markup', $template->getAttribute($template1, 'empty'));
+        $this->assertSame('', $template->getAttribute($template1, 'empty'));
+    }
+
+    public function getGetAttributeWithTemplateAsObject()
+    {
+        $bools = array(
+            array(false),
+        );
+
+        if (function_exists('twig_template_get_attributes')) {
+            $bools[] = array(true);
+        }
+
+        return $bools;
+    }
+
+    /**
+     * @dataProvider getTestsDependingOnExtensionAvailability
+     */
+    public function testGetAttributeOnArrayWithConfusableKey($useExt = false)
+    {
+        $template = new Twig_TemplateTest(
+            new Twig_Environment(),
+            $useExt
+        );
+
+        $array = array('Zero', 'One', -1 => 'MinusOne', '' => 'EmptyString', '1.5' => 'FloatButString', '01' => 'IntegerButStringWithLeadingZeros');
+
+        $this->assertSame('Zero', $array[false]);
+        $this->assertSame('One', $array[true]);
+        $this->assertSame('One', $array[1.5]);
+        $this->assertSame('One', $array['1']);
+        $this->assertSame('MinusOne', $array[-1.5]);
+        $this->assertSame('FloatButString', $array['1.5']);
+        $this->assertSame('IntegerButStringWithLeadingZeros', $array['01']);
+        $this->assertSame('EmptyString', $array[null]);
+
+        $this->assertSame('Zero', $template->getAttribute($array, false), 'false is treated as 0 when accessing an array (equals PHP behavior)');
+        $this->assertSame('One', $template->getAttribute($array, true), 'true is treated as 1 when accessing an array (equals PHP behavior)');
+        $this->assertSame('One', $template->getAttribute($array, 1.5), 'float is casted to int when accessing an array (equals PHP behavior)');
+        $this->assertSame('One', $template->getAttribute($array, '1'), '"1" is treated as integer 1 when accessing an array (equals PHP behavior)');
+        $this->assertSame('MinusOne', $template->getAttribute($array, -1.5), 'negative float is casted to int when accessing an array (equals PHP behavior)');
+        $this->assertSame('FloatButString', $template->getAttribute($array, '1.5'), '"1.5" is treated as-is when accessing an array (equals PHP behavior)');
+        $this->assertSame('IntegerButStringWithLeadingZeros', $template->getAttribute($array, '01'), '"01" is treated as-is when accessing an array (equals PHP behavior)');
+        $this->assertSame('EmptyString', $template->getAttribute($array, null), 'null is treated as "" when accessing an array (equals PHP behavior)');
+    }
+
+    public function getTestsDependingOnExtensionAvailability()
+    {
+        if (function_exists('twig_template_get_attributes')) {
+            return array(array(false), array(true));
+        }
+
+        return array(array(false));
+    }
+
+    /**
+     * @dataProvider getGetAttributeTests
+     */
+    public function testGetAttribute($defined, $value, $object, $item, $arguments, $type, $useExt = false)
+    {
+        $template = new Twig_TemplateTest(new Twig_Environment(), $useExt);
+
+        $this->assertEquals($value, $template->getAttribute($object, $item, $arguments, $type));
+    }
+
+    /**
+     * @dataProvider getGetAttributeTests
+     */
+    public function testGetAttributeStrict($defined, $value, $object, $item, $arguments, $type, $useExt = false, $exceptionMessage = null)
+    {
+        $template = new Twig_TemplateTest(new Twig_Environment(null, array('strict_variables' => true)), $useExt);
+
+        if ($defined) {
+            $this->assertEquals($value, $template->getAttribute($object, $item, $arguments, $type));
+        } else {
+            try {
+                $this->assertEquals($value, $template->getAttribute($object, $item, $arguments, $type));
+
+                throw new Exception('Expected Twig_Error_Runtime exception.');
+            } catch (Twig_Error_Runtime $e) {
+                if (null !== $exceptionMessage) {
+                    $this->assertSame($exceptionMessage, $e->getMessage());
+                }
+            }
+        }
+    }
+
+    /**
+     * @dataProvider getGetAttributeTests
+     */
+    public function testGetAttributeDefined($defined, $value, $object, $item, $arguments, $type, $useExt = false)
+    {
+        $template = new Twig_TemplateTest(new Twig_Environment(), $useExt);
+
+        $this->assertEquals($defined, $template->getAttribute($object, $item, $arguments, $type, true));
+    }
+
+    /**
+     * @dataProvider getGetAttributeTests
+     */
+    public function testGetAttributeDefinedStrict($defined, $value, $object, $item, $arguments, $type, $useExt = false)
+    {
+        $template = new Twig_TemplateTest(new Twig_Environment(null, array('strict_variables' => true)), $useExt);
+
+        $this->assertEquals($defined, $template->getAttribute($object, $item, $arguments, $type, true));
+    }
+
+    public function getGetAttributeTests()
+    {
+        $array = array(
+            'defined' => 'defined',
+            'zero'    => 0,
+            'null'    => null,
+            '1'       => 1,
+            'bar'     => true,
+            '09'      => '09',
+            '+4'      => '+4',
+        );
+
+        $objectArray         = new Twig_TemplateArrayAccessObject();
+        $stdObject           = (object) $array;
+        $magicPropertyObject = new Twig_TemplateMagicPropertyObject();
+        $propertyObject      = new Twig_TemplatePropertyObject();
+        $propertyObject1     = new Twig_TemplatePropertyObjectAndIterator();
+        $propertyObject2     = new Twig_TemplatePropertyObjectAndArrayAccess();
+        $methodObject        = new Twig_TemplateMethodObject();
+        $magicMethodObject   = new Twig_TemplateMagicMethodObject();
+
+        $anyType    = Twig_TemplateInterface::ANY_CALL;
+        $methodType = Twig_TemplateInterface::METHOD_CALL;
+        $arrayType  = Twig_TemplateInterface::ARRAY_CALL;
+
+        $basicTests = array(
+            // array(defined, value, property to fetch)
+            array(true,  'defined', 'defined'),
+            array(false, null,      'undefined'),
+            array(false, null,      'protected'),
+            array(true,  0,         'zero'),
+            array(true,  1,         1),
+            array(true,  1,         1.0),
+            array(true,  null,      'null'),
+            array(true,  true,      'bar'),
+            array(true,  '09',      '09'),
+            array(true,  '+4',      '+4'),
+        );
+        $testObjects = array(
+            // array(object, type of fetch)
+            array($array,               $arrayType),
+            array($objectArray,         $arrayType),
+            array($stdObject,           $anyType),
+            array($magicPropertyObject, $anyType),
+            array($methodObject,        $methodType),
+            array($methodObject,        $anyType),
+            array($propertyObject,      $anyType),
+            array($propertyObject1,     $anyType),
+            array($propertyObject2,     $anyType),
+        );
+
+        $tests = array();
+        foreach ($testObjects as $testObject) {
+            foreach ($basicTests as $test) {
+                // properties cannot be numbers
+                if (($testObject[0] instanceof stdClass || $testObject[0] instanceof Twig_TemplatePropertyObject) && is_numeric($test[2])) {
+                     continue;
+                }
+
+                if ('+4' === $test[2] && $methodObject === $testObject[0]) {
+                    continue;
+                }
+
+                $tests[] = array($test[0], $test[1], $testObject[0], $test[2], array(), $testObject[1]);
+            }
+        }
+
+        // additional method tests
+        $tests = array_merge($tests, array(
+            array(true, 'defined', $methodObject, 'defined',    array(), $methodType),
+            array(true, 'defined', $methodObject, 'DEFINED',    array(), $methodType),
+            array(true, 'defined', $methodObject, 'getDefined', array(), $methodType),
+            array(true, 'defined', $methodObject, 'GETDEFINED', array(), $methodType),
+            array(true, 'static',  $methodObject, 'static',     array(), $methodType),
+            array(true, 'static',  $methodObject, 'getStatic',  array(), $methodType),
+
+            array(true, '__call_undefined', $magicMethodObject, 'undefined', array(), $methodType),
+            array(true, '__call_UNDEFINED', $magicMethodObject, 'UNDEFINED', array(), $methodType),
+        ));
+
+        // add the same tests for the any type
+        foreach ($tests as $test) {
+            if ($anyType !== $test[5]) {
+                $test[5] = $anyType;
+                $tests[] = $test;
+            }
+        }
+
+        $methodAndPropObject = new Twig_TemplateMethodAndPropObject;
+
+        // additional method tests
+        $tests = array_merge($tests, array(
+            array(true, 'a', $methodAndPropObject, 'a', array(), $anyType),
+            array(true, 'a', $methodAndPropObject, 'a', array(), $methodType),
+            array(false, null, $methodAndPropObject, 'a', array(), $arrayType),
+
+            array(true, 'b_prop', $methodAndPropObject, 'b', array(), $anyType),
+            array(true, 'b', $methodAndPropObject, 'B', array(), $anyType),
+            array(true, 'b', $methodAndPropObject, 'b', array(), $methodType),
+            array(true, 'b', $methodAndPropObject, 'B', array(), $methodType),
+            array(false, null, $methodAndPropObject, 'b', array(), $arrayType),
+
+            array(false, null, $methodAndPropObject, 'c', array(), $anyType),
+            array(false, null, $methodAndPropObject, 'c', array(), $methodType),
+            array(false, null, $methodAndPropObject, 'c', array(), $arrayType),
+
+        ));
+
+        // tests when input is not an array or object
+        $tests = array_merge($tests, array(
+            array(false, null, 42, 'a', array(), $anyType, false, 'Impossible to access an attribute ("a") on a integer variable ("42")'),
+            array(false, null, "string", 'a', array(), $anyType, false, 'Impossible to access an attribute ("a") on a string variable ("string")'),
+            array(false, null, array(), 'a', array(), $anyType, false, 'Key "a" for array with keys "" does not exist'),
+        ));
+
+        // add twig_template_get_attributes tests
+
+        if (function_exists('twig_template_get_attributes')) {
+            foreach (array_slice($tests, 0) as $test) {
+                $test = array_pad($test, 7, null);
+                $test[6] = true;
+                $tests[] = $test;
+            }
+        }
+
+        return $tests;
+    }
+}
+
+class Twig_TemplateTest extends Twig_Template
+{
+    protected $useExtGetAttribute = false;
+
+    public function __construct(Twig_Environment $env, $useExtGetAttribute = false)
+    {
+        parent::__construct($env);
+        $this->useExtGetAttribute = $useExtGetAttribute;
+        Twig_Template::clearCache();
+    }
+
+    public function getZero()
+    {
+        return 0;
+    }
+
+    public function getEmpty()
+    {
+        return '';
+    }
+
+    public function getString()
+    {
+        return 'some_string';
+    }
+
+    public function getTrue()
+    {
+        return true;
+    }
+
+    public function getTemplateName()
+    {
+    }
+
+    public function getDebugInfo()
+    {
+        return array();
+    }
+
+    protected function doGetParent(array $context)
+    {
+    }
+
+    protected function doDisplay(array $context, array $blocks = array())
+    {
+    }
+
+    public function getAttribute($object, $item, array $arguments = array(), $type = Twig_TemplateInterface::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false)
+    {
+        if ($this->useExtGetAttribute) {
+            return twig_template_get_attributes($this, $object, $item, $arguments, $type, $isDefinedTest, $ignoreStrictCheck);
+        } else {
+            return parent::getAttribute($object, $item, $arguments, $type, $isDefinedTest, $ignoreStrictCheck);
+        }
+    }
+}
+
+class Twig_TemplateArrayAccessObject implements ArrayAccess
+{
+    protected $protected = 'protected';
+
+    public $attributes = array(
+        'defined' => 'defined',
+        'zero'    => 0,
+        'null'    => null,
+        '1'       => 1,
+        'bar'     => true,
+        '09'      => '09',
+        '+4'      => '+4',
+    );
+
+    public function offsetExists($name)
+    {
+        return array_key_exists($name, $this->attributes);
+    }
+
+    public function offsetGet($name)
+    {
+        return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : null;
+    }
+
+    public function offsetSet($name, $value)
+    {
+    }
+
+    public function offsetUnset($name)
+    {
+    }
+}
+
+class Twig_TemplateMagicPropertyObject
+{
+    public $defined = 'defined';
+
+    public $attributes = array(
+        'zero'    => 0,
+        'null'    => null,
+        '1'       => 1,
+        'bar'     => true,
+        '09'      => '09',
+        '+4'      => '+4',
+    );
+
+    protected $protected = 'protected';
+
+    public function __isset($name)
+    {
+        return array_key_exists($name, $this->attributes);
+    }
+
+    public function __get($name)
+    {
+        return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : null;
+    }
+}
+
+class Twig_TemplateMagicPropertyObjectWithException
+{
+    public function __isset($key)
+    {
+        throw new Exception("Hey! Don't try to isset me!");
+    }
+}
+
+class Twig_TemplatePropertyObject
+{
+    public $defined = 'defined';
+    public $zero    = 0;
+    public $null    = null;
+    public $bar     = true;
+
+    protected $protected = 'protected';
+}
+
+class Twig_TemplatePropertyObjectAndIterator extends Twig_TemplatePropertyObject implements IteratorAggregate
+{
+    public function getIterator()
+    {
+        return new ArrayIterator(array('foo', 'bar'));
+    }
+}
+
+class Twig_TemplatePropertyObjectAndArrayAccess extends Twig_TemplatePropertyObject implements ArrayAccess
+{
+    private $data = array();
+
+    public function offsetExists($offset)
+    {
+        return array_key_exists($offset, $this->data);
+    }
+
+    public function offsetGet($offset)
+    {
+        return $this->offsetExists($offset) ? $this->data[$offset] : 'n/a';
+    }
+
+    public function offsetSet($offset, $value)
+    {
+    }
+
+    public function offsetUnset($offset)
+    {
+    }
+}
+
+class Twig_TemplateMethodObject
+{
+    public function getDefined()
+    {
+        return 'defined';
+    }
+
+    public function get1()
+    {
+        return 1;
+    }
+
+    public function get09()
+    {
+        return '09';
+    }
+
+    public function getZero()
+    {
+        return 0;
+    }
+
+    public function getNull()
+    {
+        return null;
+    }
+
+    public function isBar()
+    {
+        return true;
+    }
+
+    protected function getProtected()
+    {
+        return 'protected';
+    }
+
+    public static function getStatic()
+    {
+        return 'static';
+    }
+}
+
+class Twig_TemplateMethodAndPropObject
+{
+    private $a = 'a_prop';
+    public function getA()
+    {
+        return 'a';
+    }
+
+    public $b = 'b_prop';
+    public function getB()
+    {
+        return 'b';
+    }
+
+    private $c = 'c_prop';
+    private function getC()
+    {
+        return 'c';
+    }
+}
+
+class Twig_TemplateMagicMethodObject
+{
+    public function __call($method, $arguments)
+    {
+        return '__call_'.$method;
+    }
+}
+
+class CExtDisablingNodeVisitor implements Twig_NodeVisitorInterface
+{
+    public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
+    {
+        if ($node instanceof Twig_Node_Expression_GetAttr) {
+            $node->setAttribute('disable_c_ext', true);
+        }
+
+        return $node;
+    }
+
+    public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
+    {
+        return $node;
+    }
+
+    public function getPriority()
+    {
+        return 0;
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/TokenStreamTest.php b/vendor/twig/twig/test/Twig/Tests/TokenStreamTest.php
new file mode 100644 (file)
index 0000000..fd4ec63
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Twig_Tests_TokenStreamTest extends PHPUnit_Framework_TestCase
+{
+    protected static $tokens;
+
+    public function setUp()
+    {
+        self::$tokens = array(
+            new Twig_Token(Twig_Token::TEXT_TYPE, 1, 1),
+            new Twig_Token(Twig_Token::TEXT_TYPE, 2, 1),
+            new Twig_Token(Twig_Token::TEXT_TYPE, 3, 1),
+            new Twig_Token(Twig_Token::TEXT_TYPE, 4, 1),
+            new Twig_Token(Twig_Token::TEXT_TYPE, 5, 1),
+            new Twig_Token(Twig_Token::TEXT_TYPE, 6, 1),
+            new Twig_Token(Twig_Token::TEXT_TYPE, 7, 1),
+            new Twig_Token(Twig_Token::EOF_TYPE, 0, 1),
+        );
+    }
+
+    public function testNext()
+    {
+        $stream = new Twig_TokenStream(self::$tokens);
+        $repr = array();
+        while (!$stream->isEOF()) {
+            $token = $stream->next();
+
+            $repr[] = $token->getValue();
+        }
+        $this->assertEquals('1, 2, 3, 4, 5, 6, 7', implode(', ', $repr), '->next() advances the pointer and returns the current token');
+    }
+
+    /**
+     * @expectedException Twig_Error_Syntax
+     * @expectedMessage   Unexpected end of template
+     */
+    public function testEndOfTemplateNext()
+    {
+        $stream = new Twig_TokenStream(array(
+            new Twig_Token(Twig_Token::BLOCK_START_TYPE, 1, 1),
+        ));
+        while (!$stream->isEOF()) {
+            $stream->next();
+        }
+    }
+
+    /**
+     * @expectedException Twig_Error_Syntax
+     * @expectedMessage   Unexpected end of template
+     */
+    public function testEndOfTemplateLook()
+    {
+        $stream = new Twig_TokenStream(array(
+            new Twig_Token(Twig_Token::BLOCK_START_TYPE, 1, 1),
+        ));
+        while (!$stream->isEOF()) {
+            $stream->look();
+            $stream->next();
+        }
+    }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/escapingTest.php b/vendor/twig/twig/test/Twig/Tests/escapingTest.php
new file mode 100644 (file)
index 0000000..b41b5f9
--- /dev/null
@@ -0,0 +1,320 @@
+<?php
+
+/**
+ * This class is adapted from code coming from Zend Framework.
+ *
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+class Twig_Test_EscapingTest extends PHPUnit_Framework_TestCase
+{
+    /**
+     * All character encodings supported by htmlspecialchars()
+     */
+    protected $htmlSpecialChars = array(
+        '\''    => '&#039;',
+        '"'     => '&quot;',
+        '<'     => '&lt;',
+        '>'     => '&gt;',
+        '&'     => '&amp;'
+    );
+
+    protected $htmlAttrSpecialChars = array(
+        '\''    => '&#x27;',
+        /* Characters beyond ASCII value 255 to unicode escape */
+        'Ā'     => '&#x0100;',
+        /* Immune chars excluded */
+        ','     => ',',
+        '.'     => '.',
+        '-'     => '-',
+        '_'     => '_',
+        /* Basic alnums excluded */
+        'a'     => 'a',
+        'A'     => 'A',
+        'z'     => 'z',
+        'Z'     => 'Z',
+        '0'     => '0',
+        '9'     => '9',
+        /* Basic control characters and null */
+        "\r"    => '&#x0D;',
+        "\n"    => '&#x0A;',
+        "\t"    => '&#x09;',
+        "\0"    => '&#xFFFD;', // should use Unicode replacement char
+        /* Encode chars as named entities where possible */
+        '<'     => '&lt;',
+        '>'     => '&gt;',
+        '&'     => '&amp;',
+        '"'     => '&quot;',
+        /* Encode spaces for quoteless attribute protection */
+        ' '     => '&#x20;',
+    );
+
+    protected $jsSpecialChars = array(
+        /* HTML special chars - escape without exception to hex */
+        '<'     => '\\x3C',
+        '>'     => '\\x3E',
+        '\''    => '\\x27',
+        '"'     => '\\x22',
+        '&'     => '\\x26',
+        /* Characters beyond ASCII value 255 to unicode escape */
+        'Ā'     => '\\u0100',
+        /* Immune chars excluded */
+        ','     => ',',
+        '.'     => '.',
+        '_'     => '_',
+        /* Basic alnums excluded */
+        'a'     => 'a',
+        'A'     => 'A',
+        'z'     => 'z',
+        'Z'     => 'Z',
+        '0'     => '0',
+        '9'     => '9',
+        /* Basic control characters and null */
+        "\r"    => '\\x0D',
+        "\n"    => '\\x0A',
+        "\t"    => '\\x09',
+        "\0"    => '\\x00',
+        /* Encode spaces for quoteless attribute protection */
+        ' '     => '\\x20',
+    );
+
+    protected $urlSpecialChars = array(
+        /* HTML special chars - escape without exception to percent encoding */
+        '<'     => '%3C',
+        '>'     => '%3E',
+        '\''    => '%27',
+        '"'     => '%22',
+        '&'     => '%26',
+        /* Characters beyond ASCII value 255 to hex sequence */
+        'Ā'     => '%C4%80',
+        /* Punctuation and unreserved check */
+        ','     => '%2C',
+        '.'     => '.',
+        '_'     => '_',
+        '-'     => '-',
+        ':'     => '%3A',
+        ';'     => '%3B',
+        '!'     => '%21',
+        /* Basic alnums excluded */
+        'a'     => 'a',
+        'A'     => 'A',
+        'z'     => 'z',
+        'Z'     => 'Z',
+        '0'     => '0',
+        '9'     => '9',
+        /* Basic control characters and null */
+        "\r"    => '%0D',
+        "\n"    => '%0A',
+        "\t"    => '%09',
+        "\0"    => '%00',
+        /* PHP quirks from the past */
+        ' '     => '%20',
+        '~'     => '~',
+        '+'     => '%2B',
+    );
+
+    protected $cssSpecialChars = array(
+        /* HTML special chars - escape without exception to hex */
+        '<'     => '\\3C ',
+        '>'     => '\\3E ',
+        '\''    => '\\27 ',
+        '"'     => '\\22 ',
+        '&'     => '\\26 ',
+        /* Characters beyond ASCII value 255 to unicode escape */
+        'Ā'     => '\\100 ',
+        /* Immune chars excluded */
+        ','     => '\\2C ',
+        '.'     => '\\2E ',
+        '_'     => '\\5F ',
+        /* Basic alnums excluded */
+        'a'     => 'a',
+        'A'     => 'A',
+        'z'     => 'z',
+        'Z'     => 'Z',
+        '0'     => '0',
+        '9'     => '9',
+        /* Basic control characters and null */
+        "\r"    => '\\D ',
+        "\n"    => '\\A ',
+        "\t"    => '\\9 ',
+        "\0"    => '\\0 ',
+        /* Encode spaces for quoteless attribute protection */
+        ' '     => '\\20 ',
+    );
+
+    protected $env;
+
+    public function setUp()
+    {
+        $this->env = new Twig_Environment();
+    }
+
+    public function testHtmlEscapingConvertsSpecialChars()
+    {
+        foreach ($this->htmlSpecialChars as $key => $value) {
+            $this->assertEquals($value, twig_escape_filter($this->env, $key, 'html'), 'Failed to escape: '.$key);
+        }
+    }
+
+    public function testHtmlAttributeEscapingConvertsSpecialChars()
+    {
+        foreach ($this->htmlAttrSpecialChars as $key => $value) {
+            $this->assertEquals($value, twig_escape_filter($this->env, $key, 'html_attr'), 'Failed to escape: '.$key);
+        }
+    }
+
+    public function testJavascriptEscapingConvertsSpecialChars()
+    {
+        foreach ($this->jsSpecialChars as $key => $value) {
+            $this->assertEquals($value, twig_escape_filter($this->env, $key, 'js'), 'Failed to escape: '.$key);
+        }
+    }
+
+    public function testJavascriptEscapingReturnsStringIfZeroLength()
+    {
+        $this->assertEquals('', twig_escape_filter($this->env, '', 'js'));
+    }
+
+    public function testJavascriptEscapingReturnsStringIfContainsOnlyDigits()
+    {
+        $this->assertEquals('123', twig_escape_filter($this->env, '123', 'js'));
+    }
+
+    public function testCssEscapingConvertsSpecialChars()
+    {
+        foreach ($this->cssSpecialChars as $key => $value) {
+            $this->assertEquals($value, twig_escape_filter($this->env, $key, 'css'), 'Failed to escape: '.$key);
+        }
+    }
+
+    public function testCssEscapingReturnsStringIfZeroLength()
+    {
+        $this->assertEquals('', twig_escape_filter($this->env, '', 'css'));
+    }
+
+    public function testCssEscapingReturnsStringIfContainsOnlyDigits()
+    {
+        $this->assertEquals('123', twig_escape_filter($this->env, '123', 'css'));
+    }
+
+    public function testUrlEscapingConvertsSpecialChars()
+    {
+        foreach ($this->urlSpecialChars as $key => $value) {
+            $this->assertEquals($value, twig_escape_filter($this->env, $key, 'url'), 'Failed to escape: '.$key);
+        }
+    }
+
+    /**
+     * Range tests to confirm escaped range of characters is within OWASP recommendation
+     */
+
+    /**
+     * Only testing the first few 2 ranges on this prot. function as that's all these
+     * other range tests require
+     */
+    public function testUnicodeCodepointConversionToUtf8()
+    {
+        $expected = " ~ޙ";
+        $codepoints = array(0x20, 0x7e, 0x799);
+        $result = '';
+        foreach ($codepoints as $value) {
+            $result .= $this->codepointToUtf8($value);
+        }
+        $this->assertEquals($expected, $result);
+    }
+
+    /**
+     * Convert a Unicode Codepoint to a literal UTF-8 character.
+     *
+     * @param int Unicode codepoint in hex notation
+     * @return string UTF-8 literal string
+     */
+    protected function codepointToUtf8($codepoint)
+    {
+        if ($codepoint < 0x80) {
+            return chr($codepoint);
+        }
+        if ($codepoint < 0x800) {
+            return chr($codepoint >> 6 & 0x3f | 0xc0)
+                . chr($codepoint & 0x3f | 0x80);
+        }
+        if ($codepoint < 0x10000) {
+            return chr($codepoint >> 12 & 0x0f | 0xe0)
+                . chr($codepoint >> 6 & 0x3f | 0x80)
+                . chr($codepoint & 0x3f | 0x80);
+        }
+        if ($codepoint < 0x110000) {
+            return chr($codepoint >> 18 & 0x07 | 0xf0)
+                . chr($codepoint >> 12 & 0x3f | 0x80)
+                . chr($codepoint >> 6 & 0x3f | 0x80)
+                . chr($codepoint & 0x3f | 0x80);
+        }
+        throw new Exception('Codepoint requested outside of Unicode range');
+    }
+
+    public function testJavascriptEscapingEscapesOwaspRecommendedRanges()
+    {
+        $immune = array(',', '.', '_'); // Exceptions to escaping ranges
+        for ($chr=0; $chr < 0xFF; $chr++) {
+            if ($chr >= 0x30 && $chr <= 0x39
+            || $chr >= 0x41 && $chr <= 0x5A
+            || $chr >= 0x61 && $chr <= 0x7A) {
+                $literal = $this->codepointToUtf8($chr);
+                $this->assertEquals($literal, twig_escape_filter($this->env, $literal, 'js'));
+            } else {
+                $literal = $this->codepointToUtf8($chr);
+                if (in_array($literal, $immune)) {
+                    $this->assertEquals($literal, twig_escape_filter($this->env, $literal, 'js'));
+                } else {
+                    $this->assertNotEquals(
+                        $literal,
+                        twig_escape_filter($this->env, $literal, 'js'),
+                        "$literal should be escaped!");
+                }
+            }
+        }
+    }
+
+    public function testHtmlAttributeEscapingEscapesOwaspRecommendedRanges()
+    {
+        $immune = array(',', '.', '-', '_'); // Exceptions to escaping ranges
+        for ($chr=0; $chr < 0xFF; $chr++) {
+            if ($chr >= 0x30 && $chr <= 0x39
+            || $chr >= 0x41 && $chr <= 0x5A
+            || $chr >= 0x61 && $chr <= 0x7A) {
+                $literal = $this->codepointToUtf8($chr);
+                $this->assertEquals($literal, twig_escape_filter($this->env, $literal, 'html_attr'));
+            } else {
+                $literal = $this->codepointToUtf8($chr);
+                if (in_array($literal, $immune)) {
+                    $this->assertEquals($literal, twig_escape_filter($this->env, $literal, 'html_attr'));
+                } else {
+                    $this->assertNotEquals(
+                        $literal,
+                        twig_escape_filter($this->env, $literal, 'html_attr'),
+                        "$literal should be escaped!");
+                }
+            }
+        }
+    }
+
+    public function testCssEscapingEscapesOwaspRecommendedRanges()
+    {
+        $immune = array(); // CSS has no exceptions to escaping ranges
+        for ($chr=0; $chr < 0xFF; $chr++) {
+            if ($chr >= 0x30 && $chr <= 0x39
+            || $chr >= 0x41 && $chr <= 0x5A
+            || $chr >= 0x61 && $chr <= 0x7A) {
+                $literal = $this->codepointToUtf8($chr);
+                $this->assertEquals($literal, twig_escape_filter($this->env, $literal, 'css'));
+            } else {
+                $literal = $this->codepointToUtf8($chr);
+                $this->assertNotEquals(
+                    $literal,
+                    twig_escape_filter($this->env, $literal, 'css'),
+                    "$literal should be escaped!");
+            }
+        }
+    }
+}
diff --git a/vendor/twig/twig/test/bootstrap.php b/vendor/twig/twig/test/bootstrap.php
new file mode 100644 (file)
index 0000000..aecb976
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+require_once dirname(__FILE__).'/../lib/Twig/Autoloader.php';
+Twig_Autoloader::register(true);
diff --git a/vendor/umpirsky/twig-gettext-extractor/.gitignore b/vendor/umpirsky/twig-gettext-extractor/.gitignore
new file mode 100644 (file)
index 0000000..61381e4
--- /dev/null
@@ -0,0 +1,3 @@
+vendor
+phpunit.xml
+composer.lock
diff --git a/vendor/umpirsky/twig-gettext-extractor/.travis.yml b/vendor/umpirsky/twig-gettext-extractor/.travis.yml
new file mode 100644 (file)
index 0000000..0c9bce0
--- /dev/null
@@ -0,0 +1,10 @@
+language: php
+
+before_script:
+  - curl -s http://getcomposer.org/installer | php
+  - php composer.phar install --dev
+
+php:
+  - 5.3
+  - 5.4
+
diff --git a/vendor/umpirsky/twig-gettext-extractor/LICENSE b/vendor/umpirsky/twig-gettext-extractor/LICENSE
new file mode 100644 (file)
index 0000000..df9dd10
--- /dev/null
@@ -0,0 +1,19 @@
+Copyright (c) Саша Стаменковић <umpirsky@gmail.com>
+
+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.
\ No newline at end of file
diff --git a/vendor/umpirsky/twig-gettext-extractor/README.md b/vendor/umpirsky/twig-gettext-extractor/README.md
new file mode 100644 (file)
index 0000000..34eff88
--- /dev/null
@@ -0,0 +1,49 @@
+Twig Gettext Extractor [![Build Status](https://secure.travis-ci.org/umpirsky/Twig-Gettext-Extractor.png?branch=master)](http://travis-ci.org/umpirsky/Twig-Gettext-Extractor)
+======================
+
+The Twig Gettext Extractor is [Poedit](http://www.poedit.net/download.php)
+friendly tool which extracts translations from twig templates.
+
+## Installation
+
+The recommended way to install Twig Gettext Extractor is through
+[composer](http://getcomposer.org).
+
+```json
+{
+    "require": {
+        "umpirsky/twig-gettext-extractor": "1.1.*"
+    }
+}
+```
+
+## Setup
+
+By default, Poedit does not have the ability to parse Twig templates.
+This can be resolved by adding an additional parser (Edit > Preferences > Parsers)
+with the following options:
+
+- Language: `Twig`
+- List of extensions: `*.twig`
+- Invocation:
+    - Parser command: `<project>/vendor/bin/twig-gettext-extractor --sort-output --force-po -o %o %C %K -L PHP --files %F`
+    - An item in keyword list: `-k%k`
+    - An item in input file list: `%f`
+    - Source code charset: `--from-code=%c`
+
+<img src="http://i.imgur.com/f9px2.png" />
+
+Now you can update your catalog and Poedit will synchronize it with your twig
+templates.
+
+## Tests
+
+To run the test suite, you need [composer](http://getcomposer.org) and
+[PHPUnit](https://github.com/sebastianbergmann/phpunit).
+
+    $ composer install --dev
+    $ phpunit
+
+## License
+
+Twig Gettext Extractor is licensed under the MIT license.
diff --git a/vendor/umpirsky/twig-gettext-extractor/composer.json b/vendor/umpirsky/twig-gettext-extractor/composer.json
new file mode 100644 (file)
index 0000000..7cda5f7
--- /dev/null
@@ -0,0 +1,30 @@
+{
+    "name": "umpirsky/twig-gettext-extractor",
+    "type": "application",
+    "description": "The Twig Gettext Extractor is Poedit friendly tool which extracts translations from twig templates.",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Саша Стаменковић",
+            "email": "umpirsky@gmail.com"
+        }
+    ],
+    "require": {
+        "php":                 ">=5.3.3",
+        "twig/twig":           ">=1.2.0,<2.0-dev",
+        "twig/extensions":     "1.0.*",
+        "symfony/twig-bridge": ">=2.0,<3.0",
+        "symfony/routing":     ">=2.0,<3.0",
+        "symfony/filesystem":  ">=2.0,<3.0",
+        "symfony/translation": ">=2.0,<3.0",
+        "symfony/form":        ">=2.0,<3.0"
+    },
+    "require-dev": {
+        "symfony/config":      "2.1.*"
+    },
+    "minimum-stability": "dev",
+    "autoload": {
+        "psr-0": { "Twig\\Gettext": "." }
+    },
+    "bin": ["twig-gettext-extractor"]
+}
\ No newline at end of file
diff --git a/vendor/umpirsky/twig-gettext-extractor/phpunit.xml.dist b/vendor/umpirsky/twig-gettext-extractor/phpunit.xml.dist
new file mode 100644 (file)
index 0000000..56fdc6b
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         bootstrap="./vendor/autoload.php"
+>
+    <testsuites>
+        <testsuite name="Twig Gettext Extractor Test Suite">
+            <directory>./Twig/Gettext/Test/</directory>
+        </testsuite>
+    </testsuites>
+</phpunit>
diff --git a/vendor/umpirsky/twig-gettext-extractor/twig-gettext-extractor b/vendor/umpirsky/twig-gettext-extractor/twig-gettext-extractor
new file mode 100755 (executable)
index 0000000..6cc97c1
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/env php
+<?php
+
+/**
+ * This file is part of the Twig Gettext utility.
+ *
+ *  (c) Саша Стаменковић <umpirsky@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Extracts translations from twig templates.
+ *
+ * @author Саша Стаменковић <umpirsky@gmail.com>
+ */
+
+if (file_exists($a = __DIR__.'/../../autoload.php')) {
+    require_once $a;
+} else {
+    require_once __DIR__.'/vendor/autoload.php';
+}
+
+$twig = new Twig_Environment(new Twig\Gettext\Loader\Filesystem('/'), array(
+    'cache'       => '/tmp/cache/'.uniqid(),
+    'auto_reload' => true
+));
+$twig->addExtension(new Symfony\Bridge\Twig\Extension\TranslationExtension(
+    new Symfony\Component\Translation\Translator(null)
+));
+$twig->addExtension(new Twig_Extensions_Extension_I18n());
+$twig->addExtension(new Symfony\Bridge\Twig\Extension\RoutingExtension(
+    new Twig\Gettext\Routing\Generator\UrlGenerator()
+));
+$twig->addExtension(new Symfony\Bridge\Twig\Extension\FormExtension(
+    new Symfony\Bridge\Twig\Form\TwigRenderer(
+        new Symfony\Bridge\Twig\Form\TwigRendererEngine()
+    )
+));
+// You can add more extensions here.
+
+array_shift($_SERVER['argv']);
+$addTemplate = false;
+
+$extractor = new Twig\Gettext\Extractor($twig);
+
+foreach ($_SERVER['argv'] as $arg) {
+    if ('--files' == $arg) {
+        $addTemplate = true;
+    } else if ($addTemplate) {
+        $extractor->addTemplate(getcwd().DIRECTORY_SEPARATOR.$arg);
+    } else {
+        $extractor->addGettextParameter($arg);
+    }
+}
+
+$extractor->extract();